Skip to content

Commit 328a498

Browse files
authored
docs[minor]: Add thumbs up/down to all docs pages (#18526)
1 parent 10874d5 commit 328a498

File tree

7 files changed

+13549
-1
lines changed

7 files changed

+13549
-1
lines changed

.github/workflows/codespell.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ jobs:
3232
- name: Codespell
3333
uses: codespell-project/actions-codespell@v2
3434
with:
35-
skip: guide_imports.json,*.ambr,./cookbook/data/imdb_top_1000.csv
35+
skip: guide_imports.json,*.ambr,./cookbook/data/imdb_top_1000.csv,*.lock
3636
ignore_words_list: ${{ steps.extract_ignore_words.outputs.ignore_words_list }}
3737
exclude_file: libs/community/langchain_community/llms/yuan2.py

docs/.yarnrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nodeLinker: node-modules

docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@docusaurus/theme-mermaid": "2.4.3",
2626
"@mdx-js/react": "^1.6.22",
2727
"clsx": "^1.2.1",
28+
"cookie": "^0.6.0",
2829
"json-loader": "^0.5.7",
2930
"process": "^0.11.10",
3031
"react": "^17.0.2",

docs/src/analytics.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default function gtag(...args) {
2+
if (window.gtag) {
3+
window.gtag(...args);
4+
}
5+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from "react";
2+
import Content from "@theme-original/DocItem/Content";
3+
import Feedback from "../../Feedback";
4+
5+
export default function ContentWrapper(props) {
6+
return (
7+
<>
8+
{/* eslint-disable react/jsx-props-no-spreading */}
9+
<Content {...props} />
10+
<Feedback />
11+
</>
12+
);
13+
}

docs/src/theme/Feedback.js

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/* eslint-disable no-return-assign, react/jsx-props-no-spreading */
2+
import React, { useState, useEffect } from "react";
3+
import gtag from "../analytics";
4+
5+
const useCookie = () => {
6+
/**
7+
* Function to set a cookie
8+
* @param {string} name The name of the cookie to set
9+
* @param {string} value The value of the cookie
10+
* @param {number} days the number of days until the cookie expires
11+
*/
12+
const setCookie = (name, value, days) => {
13+
const d = new Date();
14+
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
15+
const expires = `expires=${d.toUTCString()}`;
16+
document.cookie = `${name}=${value};${expires};path=/`;
17+
};
18+
19+
/**
20+
* Function to get a cookie by name
21+
* @param {string} name The name of the cookie to get
22+
* @returns {string} The value of the cookie
23+
*/
24+
const getCookie = (name) => {
25+
const ca = document.cookie.split(";");
26+
const caLen = ca.length;
27+
const cookieName = `${name}=`;
28+
let c;
29+
30+
for (let i = 0; i < caLen; i += 1) {
31+
c = ca[i].replace(/^\s+/g, "");
32+
if (c.indexOf(cookieName) === 0) {
33+
return c.substring(cookieName.length, c.length);
34+
}
35+
}
36+
return "";
37+
};
38+
39+
/**
40+
* Function to check cookie existence
41+
* @param {string} name The name of the cookie to check for
42+
* @returns {boolean} Whether or not the cookie exists
43+
*/
44+
const checkCookie = (name) => {
45+
const cookie = getCookie(name);
46+
if (cookie !== "") {
47+
return true;
48+
}
49+
return false;
50+
};
51+
52+
return { setCookie, checkCookie };
53+
};
54+
55+
function SvgThumbsUp() {
56+
return (
57+
<svg
58+
xmlns="http://www.w3.org/2000/svg"
59+
fill="none"
60+
viewBox="0 0 24 24"
61+
strokeWidth="1.5"
62+
stroke="#166534"
63+
style={{ width: "24px", height: "24px" }}
64+
>
65+
<path
66+
strokeLinecap="round"
67+
strokeLinejoin="round"
68+
d="M6.633 10.25c.806 0 1.533-.446 2.031-1.08a9.041 9.041 0 0 1 2.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 0 0 .322-1.672V2.75a.75.75 0 0 1 .75-.75 2.25 2.25 0 0 1 2.25 2.25c0 1.152-.26 2.243-.723 3.218-.266.558.107 1.282.725 1.282m0 0h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068 1.285a11.95 11.95 0 0 1-2.649 7.521c-.388.482-.987.729-1.605.729H13.48c-.483 0-.964-.078-1.423-.23l-3.114-1.04a4.501 4.501 0 0 0-1.423-.23H5.904m10.598-9.75H14.25M5.904 18.5c.083.205.173.405.27.602.197.4-.078.898-.523.898h-.908c-.889 0-1.713-.518-1.972-1.368a12 12 0 0 1-.521-3.507c0-1.553.295-3.036.831-4.398C3.387 9.953 4.167 9.5 5 9.5h1.053c.472 0 .745.556.5.96a8.958 8.958 0 0 0-1.302 4.665c0 1.194.232 2.333.654 3.375Z"
69+
/>
70+
</svg>
71+
);
72+
}
73+
74+
function SvgThumbsDown() {
75+
return (
76+
<svg
77+
xmlns="http://www.w3.org/2000/svg"
78+
fill="none"
79+
viewBox="0 0 24 24"
80+
strokeWidth="1.5"
81+
stroke="#991b1b"
82+
style={{ width: "24px", height: "24px" }}
83+
>
84+
<path
85+
strokeLinecap="round"
86+
strokeLinejoin="round"
87+
d="M7.498 15.25H4.372c-1.026 0-1.945-.694-2.054-1.715a12.137 12.137 0 0 1-.068-1.285c0-2.848.992-5.464 2.649-7.521C5.287 4.247 5.886 4 6.504 4h4.016a4.5 4.5 0 0 1 1.423.23l3.114 1.04a4.5 4.5 0 0 0 1.423.23h1.294M7.498 15.25c.618 0 .991.724.725 1.282A7.471 7.471 0 0 0 7.5 19.75 2.25 2.25 0 0 0 9.75 22a.75.75 0 0 0 .75-.75v-.633c0-.573.11-1.14.322-1.672.304-.76.93-1.33 1.653-1.715a9.04 9.04 0 0 0 2.86-2.4c.498-.634 1.226-1.08 2.032-1.08h.384m-10.253 1.5H9.7m8.075-9.75c.01.05.027.1.05.148.593 1.2.925 2.55.925 3.977 0 1.487-.36 2.89-.999 4.125m.023-8.25c-.076-.365.183-.75.575-.75h.908c.889 0 1.713.518 1.972 1.368.339 1.11.521 2.287.521 3.507 0 1.553-.295 3.036-.831 4.398-.306.774-1.086 1.227-1.918 1.227h-1.053c-.472 0-.745-.556-.5-.96a8.95 8.95 0 0 0 .303-.54"
88+
/>
89+
</svg>
90+
);
91+
}
92+
93+
const FEEDBACK_COOKIE_PREFIX = "feedbackSent";
94+
95+
export default function Feedback() {
96+
const { setCookie, checkCookie } = useCookie();
97+
const [feedbackSent, setFeedbackSent] = useState(false);
98+
99+
/**
100+
* @param {"yes" | "no"} feedback
101+
*/
102+
const handleFeedback = (feedback) => {
103+
const cookieName = `${FEEDBACK_COOKIE_PREFIX}_${window.location.pathname}`;
104+
if (checkCookie(cookieName)) {
105+
return;
106+
}
107+
108+
const feedbackType =
109+
process.env.NODE_ENV === "production"
110+
? "page_feedback"
111+
: "page_feedback_dev";
112+
113+
gtag("event", feedbackType, {
114+
url: window.location.pathname,
115+
feedback,
116+
});
117+
// Set a cookie to prevent feedback from being sent multiple times
118+
setCookie(cookieName, window.location.pathname, 1);
119+
setFeedbackSent(true);
120+
};
121+
122+
useEffect(() => {
123+
if (typeof window !== "undefined") {
124+
// If the cookie exists, set feedback sent to
125+
// true so the user can not send feedback again
126+
// (cookies exp in 24hrs)
127+
const cookieName = `${FEEDBACK_COOKIE_PREFIX}_${window.location.pathname}`;
128+
setFeedbackSent(checkCookie(cookieName));
129+
}
130+
}, []);
131+
132+
const defaultFields = {
133+
style: {
134+
display: "flex",
135+
alignItems: "center",
136+
paddingTop: "10px",
137+
paddingBottom: "10px",
138+
paddingLeft: "22px",
139+
paddingRight: "22px",
140+
border: "1px solid gray",
141+
borderRadius: "6px",
142+
gap: "10px",
143+
cursor: "pointer",
144+
fontSize: "16px",
145+
fontWeight: "600",
146+
},
147+
onMouseEnter: (e) => (e.currentTarget.style.backgroundColor = "#f0f0f0"),
148+
onMouseLeave: (e) =>
149+
(e.currentTarget.style.backgroundColor = "transparent"),
150+
onMouseDown: (e) => (e.currentTarget.style.backgroundColor = "#d0d0d0"),
151+
onMouseUp: (e) => (e.currentTarget.style.backgroundColor = "#f0f0f0"),
152+
};
153+
154+
return (
155+
<div style={{ display: "flex", flexDirection: "column" }}>
156+
<hr />
157+
{feedbackSent ? (
158+
<h4>Thanks for your feedback!</h4>
159+
) : (
160+
<>
161+
<h4>Help us out by providing feedback on this documentation page:</h4>
162+
<div style={{ display: "flex", gap: "5px" }}>
163+
<div
164+
{...defaultFields}
165+
role="button" // Make it recognized as an interactive element
166+
tabIndex={0} // Make it focusable
167+
onKeyDown={(e) => {
168+
// Handle keyboard interaction
169+
if (e.key === "Enter" || e.key === " ") {
170+
e.preventDefault();
171+
handleFeedback("yes");
172+
}
173+
}}
174+
onClick={(e) => {
175+
e.preventDefault();
176+
handleFeedback("yes");
177+
}}
178+
>
179+
<SvgThumbsUp />
180+
</div>
181+
<div
182+
{...defaultFields}
183+
role="button" // Make it recognized as an interactive element
184+
tabIndex={0} // Make it focusable
185+
onKeyDown={(e) => {
186+
// Handle keyboard interaction
187+
if (e.key === "Enter" || e.key === " ") {
188+
e.preventDefault();
189+
handleFeedback("no");
190+
}
191+
}}
192+
onClick={(e) => {
193+
e.preventDefault();
194+
handleFeedback("no");
195+
}}
196+
>
197+
<SvgThumbsDown />
198+
</div>
199+
</div>
200+
</>
201+
)}
202+
</div>
203+
);
204+
}

0 commit comments

Comments
 (0)