@redocly/theme 0.6.5 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Feedback/Comment.d.ts +3 -0
- package/lib/Feedback/Comment.js +80 -0
- package/lib/Feedback/Rating.js +23 -10
- package/lib/Feedback/Reasons.d.ts +3 -0
- package/lib/Feedback/Reasons.js +85 -0
- package/lib/Feedback/Sentiment.js +28 -4
- package/lib/Feedback/index.d.ts +4 -2
- package/lib/Feedback/index.js +7 -3
- package/lib/Feedback/types.d.ts +48 -3
- package/lib/Markdown/Tabs/Tab.js +11 -5
- package/lib/Markdown/Tabs/Tabs.js +14 -5
- package/lib/Navbar/Navbar.js +3 -1
- package/lib/Pages/Forbidden.d.ts +2 -0
- package/lib/Pages/Forbidden.js +39 -0
- package/lib/Pages/NotFound.d.ts +2 -0
- package/lib/Pages/NotFound.js +39 -0
- package/lib/Pages/index.d.ts +1 -0
- package/lib/Pages/index.js +18 -0
- package/lib/Search/Autocomplete.d.ts +4 -1
- package/lib/Search/Autocomplete.js +19 -3
- package/lib/Search/ClearIcon.js +1 -1
- package/lib/Search/Input.js +1 -1
- package/lib/Search/Search.js +6 -1
- package/lib/Search/SearchIcon.js +1 -1
- package/lib/Search/ShortcutKey.d.ts +7 -0
- package/lib/Search/ShortcutKey.js +35 -0
- package/lib/config.d.ts +11 -0
- package/lib/config.js +2 -0
- package/lib/globalStyle.js +59 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/mocks/Link.js +1 -1
- package/lib/mocks/hooks/index.js +5 -1
- package/lib/mocks/search.js +18 -5
- package/lib/ui/darkColors.js +5 -0
- package/package.json +8 -4
- package/src/Feedback/Comment.tsx +64 -0
- package/src/Feedback/Rating.tsx +45 -17
- package/src/Feedback/Reasons.tsx +81 -0
- package/src/Feedback/Sentiment.tsx +44 -5
- package/src/Feedback/index.ts +4 -2
- package/src/Feedback/types.ts +37 -3
- package/src/Markdown/Tabs/Tab.tsx +11 -5
- package/src/Markdown/Tabs/Tabs.tsx +14 -5
- package/src/Navbar/Navbar.tsx +5 -1
- package/src/Pages/Forbidden.tsx +42 -0
- package/src/Pages/NotFound.tsx +42 -0
- package/src/Pages/index.ts +1 -0
- package/src/Search/Autocomplete.tsx +26 -2
- package/src/Search/ClearIcon.tsx +1 -1
- package/src/Search/Input.tsx +1 -1
- package/src/Search/Search.tsx +3 -0
- package/src/Search/SearchIcon.tsx +1 -1
- package/src/Search/ShortcutKey.tsx +35 -0
- package/src/config.ts +5 -0
- package/src/globalStyle.ts +60 -1
- package/src/index.ts +1 -0
- package/src/mocks/Link.tsx +2 -1
- package/src/mocks/hooks/index.ts +5 -1
- package/src/mocks/search.ts +20 -5
- package/src/ui/darkColors.tsx +5 -0
package/lib/Search/Search.js
CHANGED
|
@@ -35,12 +35,17 @@ const Autocomplete_1 = require("../Search/Autocomplete");
|
|
|
35
35
|
const ClearIcon_1 = require("../Search/ClearIcon");
|
|
36
36
|
const SearchIcon_1 = require("../Search/SearchIcon");
|
|
37
37
|
const SearchItem_1 = require("../Search/SearchItem");
|
|
38
|
+
const hooks_1 = require("../hooks");
|
|
38
39
|
function Search() {
|
|
39
40
|
const history = (0, usePreloadHistory_1.usePreloadHistory)();
|
|
40
41
|
const { query, setQuery, items } = (0, search_1.useFuseSearch)();
|
|
42
|
+
const themeSettings = (0, hooks_1.useThemeConfig)();
|
|
41
43
|
// TODO: ask somebody about typings
|
|
42
44
|
const navigate = (item) => history.push(item.url);
|
|
43
|
-
const renderAutocomplete = () =>
|
|
45
|
+
const renderAutocomplete = () => {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
return (React.createElement(Autocomplete_1.Autocomplete, { items: items, value: query, change: setQuery, select: navigate, placeholder: "Search the docs", keyShortcuts: (_b = (_a = themeSettings === null || themeSettings === void 0 ? void 0 : themeSettings.search) === null || _a === void 0 ? void 0 : _a.shortcuts) !== null && _b !== void 0 ? _b : ['/'], renderItem: (item) => React.createElement(SearchItem_1.SearchItem, { key: item.id, item: item }) }, (isOpen, reset) => (isOpen ? React.createElement(ClearIcon_1.ClearIcon, { onClick: reset }) : React.createElement(SearchIcon_1.SearchIcon, null))));
|
|
48
|
+
};
|
|
44
49
|
return React.createElement(Wrapper, { "data-component-name": "Search/Search" }, renderAutocomplete());
|
|
45
50
|
}
|
|
46
51
|
exports.Search = Search;
|
package/lib/Search/SearchIcon.js
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
interface ShortcutKeyProps {
|
|
3
|
+
keyShortcuts?: string | string[];
|
|
4
|
+
}
|
|
5
|
+
export declare function ShortcutKey(props: ShortcutKeyProps): JSX.Element;
|
|
6
|
+
export declare const Wrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Wrapper = exports.ShortcutKey = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const styled_components_1 = __importDefault(require("styled-components"));
|
|
9
|
+
function ShortcutKey(props) {
|
|
10
|
+
let mainShortcutKey = null;
|
|
11
|
+
if (props.keyShortcuts) {
|
|
12
|
+
if (Array.isArray(props.keyShortcuts)) {
|
|
13
|
+
mainShortcutKey = props.keyShortcuts[0];
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
mainShortcutKey = props.keyShortcuts;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
mainShortcutKey = mainShortcutKey === null || mainShortcutKey === void 0 ? void 0 : mainShortcutKey.toUpperCase();
|
|
20
|
+
return react_1.default.createElement(exports.Wrapper, null, mainShortcutKey);
|
|
21
|
+
}
|
|
22
|
+
exports.ShortcutKey = ShortcutKey;
|
|
23
|
+
exports.Wrapper = styled_components_1.default.div `
|
|
24
|
+
position: absolute;
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
font-size: 0.8em;
|
|
27
|
+
height: 2em;
|
|
28
|
+
line-height: 2em;
|
|
29
|
+
right: 1em;
|
|
30
|
+
fill: var(--search-input-text-color);
|
|
31
|
+
color: var(--search-input-placeholder-color);
|
|
32
|
+
opacity: 0.5;
|
|
33
|
+
z-index: -1;
|
|
34
|
+
`;
|
|
35
|
+
//# sourceMappingURL=ShortcutKey.js.map
|
package/lib/config.d.ts
CHANGED
|
@@ -244,14 +244,17 @@ export declare const ThemeConfig: z.ZodDefault<z.ZodObject<{
|
|
|
244
244
|
}>>>;
|
|
245
245
|
search: z.ZodOptional<z.ZodDefault<z.ZodObject<z.extendShape<{
|
|
246
246
|
placement: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
247
|
+
shortcuts: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
|
|
247
248
|
}, {
|
|
248
249
|
hide: z.ZodOptional<z.ZodBoolean>;
|
|
249
250
|
}>, "strict", z.ZodTypeAny, {
|
|
250
251
|
hide?: boolean | undefined;
|
|
251
252
|
placement?: string | undefined;
|
|
253
|
+
shortcuts?: string[] | undefined;
|
|
252
254
|
}, {
|
|
253
255
|
hide?: boolean | undefined;
|
|
254
256
|
placement?: string | undefined;
|
|
257
|
+
shortcuts?: string[] | undefined;
|
|
255
258
|
}>>>;
|
|
256
259
|
colorMode: z.ZodDefault<z.ZodOptional<z.ZodObject<z.extendShape<{
|
|
257
260
|
ignoreDetection: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -429,6 +432,7 @@ export declare const ThemeConfig: z.ZodDefault<z.ZodObject<{
|
|
|
429
432
|
}>>>;
|
|
430
433
|
openapi: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, {}, {}>>;
|
|
431
434
|
graphql: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, {}, {}>>;
|
|
435
|
+
analytics: z.ZodOptional<z.ZodAny>;
|
|
432
436
|
userProfile: z.ZodDefault<z.ZodOptional<z.ZodObject<z.extendShape<{
|
|
433
437
|
loginLabel: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
434
438
|
logoutLabel: z.ZodOptional<z.ZodDefault<z.ZodString>>;
|
|
@@ -519,6 +523,7 @@ export declare const ThemeConfig: z.ZodDefault<z.ZodObject<{
|
|
|
519
523
|
search?: {
|
|
520
524
|
hide?: boolean | undefined;
|
|
521
525
|
placement?: string | undefined;
|
|
526
|
+
shortcuts?: string[] | undefined;
|
|
522
527
|
} | undefined;
|
|
523
528
|
markdown?: {
|
|
524
529
|
frontMatterKeysToResolve?: string[] | undefined;
|
|
@@ -548,6 +553,7 @@ export declare const ThemeConfig: z.ZodDefault<z.ZodObject<{
|
|
|
548
553
|
} | undefined;
|
|
549
554
|
openapi?: {} | undefined;
|
|
550
555
|
graphql?: {} | undefined;
|
|
556
|
+
analytics?: any;
|
|
551
557
|
colorMode: {
|
|
552
558
|
hide?: boolean | undefined;
|
|
553
559
|
ignoreDetection?: boolean | undefined;
|
|
@@ -642,6 +648,7 @@ export declare const ThemeConfig: z.ZodDefault<z.ZodObject<{
|
|
|
642
648
|
search?: {
|
|
643
649
|
hide?: boolean | undefined;
|
|
644
650
|
placement?: string | undefined;
|
|
651
|
+
shortcuts?: string[] | undefined;
|
|
645
652
|
} | undefined;
|
|
646
653
|
colorMode?: {
|
|
647
654
|
hide?: boolean | undefined;
|
|
@@ -686,6 +693,7 @@ export declare const ThemeConfig: z.ZodDefault<z.ZodObject<{
|
|
|
686
693
|
} | undefined;
|
|
687
694
|
openapi?: {} | undefined;
|
|
688
695
|
graphql?: {} | undefined;
|
|
696
|
+
analytics?: any;
|
|
689
697
|
userProfile?: {
|
|
690
698
|
hide?: boolean | undefined;
|
|
691
699
|
loginLabel?: string | undefined;
|
|
@@ -705,4 +713,7 @@ export type ThemeUIConfig = Omit<ThemeConfig, 'navbar' | 'footer' | 'links' | 's
|
|
|
705
713
|
}[];
|
|
706
714
|
devLogin?: boolean;
|
|
707
715
|
};
|
|
716
|
+
search?: {
|
|
717
|
+
shortcuts?: string[];
|
|
718
|
+
};
|
|
708
719
|
};
|
package/lib/config.js
CHANGED
|
@@ -86,6 +86,7 @@ exports.ThemeConfig = zod_1.z
|
|
|
86
86
|
search: zod_1.z
|
|
87
87
|
.object({
|
|
88
88
|
placement: zod_1.z.string().default('navbar').optional(),
|
|
89
|
+
shortcuts: zod_1.z.array(zod_1.z.string()).default(['/']).optional(),
|
|
89
90
|
})
|
|
90
91
|
.extend(HideConfig.shape)
|
|
91
92
|
.strict()
|
|
@@ -160,6 +161,7 @@ exports.ThemeConfig = zod_1.z
|
|
|
160
161
|
.optional(),
|
|
161
162
|
openapi: zod_1.z.object({}).passthrough().optional(),
|
|
162
163
|
graphql: zod_1.z.object({}).passthrough().optional(),
|
|
164
|
+
analytics: zod_1.z.any().optional(),
|
|
163
165
|
userProfile: zod_1.z
|
|
164
166
|
.object({
|
|
165
167
|
loginLabel: zod_1.z.string().default('Login').optional(),
|
package/lib/globalStyle.js
CHANGED
|
@@ -1784,6 +1784,16 @@ const markdown = (0, styled_components_1.css) `
|
|
|
1784
1784
|
--md-numbered-list-number-padding: 0 5px; // @presenter Spacing
|
|
1785
1785
|
|
|
1786
1786
|
// @tokens End
|
|
1787
|
+
|
|
1788
|
+
/**
|
|
1789
|
+
* @tokens Markdown Tabs
|
|
1790
|
+
*/
|
|
1791
|
+
|
|
1792
|
+
--md-tabs-tab-text-color: var(--color-emphasis-600); // @presenter Color
|
|
1793
|
+
--md-tabs-active-tab-border-color: var(--text-color); // @presenter Color
|
|
1794
|
+
--md-tabs-hover-tab-border-color: var(--text-color-secondary); // @presenter Color
|
|
1795
|
+
|
|
1796
|
+
// @tokens End
|
|
1787
1797
|
`;
|
|
1788
1798
|
const search = (0, styled_components_1.css) `
|
|
1789
1799
|
/**
|
|
@@ -1879,7 +1889,54 @@ const tile = (0, styled_components_1.css) `
|
|
|
1879
1889
|
--thin-tile-background-color: var(--color-secondary-50);
|
|
1880
1890
|
`;
|
|
1881
1891
|
const apiLogsTable = (0, styled_components_1.css) `
|
|
1882
|
-
--api-logs-row-hover-background-color: var(--color-secondary-300)
|
|
1892
|
+
--api-logs-row-hover-background-color: var(--color-secondary-300);
|
|
1893
|
+
`;
|
|
1894
|
+
const pages = (0, styled_components_1.css) `
|
|
1895
|
+
/**
|
|
1896
|
+
* @tokens 404 Page
|
|
1897
|
+
* @presenter Color
|
|
1898
|
+
*/
|
|
1899
|
+
|
|
1900
|
+
--page-404-font-family: var(--font-family-base); // @presenter FontFamily
|
|
1901
|
+
|
|
1902
|
+
--page-404-header-text-color: #000;
|
|
1903
|
+
--page-404-header-font-size: 14em; // @presenter FontSize
|
|
1904
|
+
--page-404-header-font-weight: 600; // @presenter FontWeight
|
|
1905
|
+
--page-404-header-line-height: 1.2; // @presenter LineHeight
|
|
1906
|
+
--page-404-header-margin: 0; // @presenter Spacing
|
|
1907
|
+
|
|
1908
|
+
--page-404-description-text-color: #000;
|
|
1909
|
+
--page-404-description-font-size: 2em; // @presenter FontSize
|
|
1910
|
+
--page-404-description-font-weight: 400; // @presenter FontWeight
|
|
1911
|
+
--page-404-description-line-height: 1; // @presenter LineHeight
|
|
1912
|
+
--page-404-description-margin: 0; // @presenter Spacing
|
|
1913
|
+
|
|
1914
|
+
--page-404-button-margin: 4em; // @presenter Spacing
|
|
1915
|
+
|
|
1916
|
+
// @tokens End
|
|
1917
|
+
|
|
1918
|
+
/**
|
|
1919
|
+
* @tokens 403 Page
|
|
1920
|
+
* @presenter Color
|
|
1921
|
+
*/
|
|
1922
|
+
|
|
1923
|
+
--page-403-font-family: var(--font-family-base); // @presenter FontFamily
|
|
1924
|
+
|
|
1925
|
+
--page-403-header-text-color: #000;
|
|
1926
|
+
--page-403-header-font-size: 14em; // @presenter FontSize
|
|
1927
|
+
--page-403-header-font-weight: 600; // @presenter FontWeight
|
|
1928
|
+
--page-403-header-line-height: 1.2; // @presenter LineHeight
|
|
1929
|
+
--page-403-header-margin: 0; // @presenter Spacing
|
|
1930
|
+
|
|
1931
|
+
--page-403-description-text-color: #000;
|
|
1932
|
+
--page-403-description-font-size: 2em; // @presenter FontSize
|
|
1933
|
+
--page-403-description-font-weight: 400; // @presenter FontWeight
|
|
1934
|
+
--page-403-description-line-height: 1; // @presenter LineHeight
|
|
1935
|
+
--page-403-description-margin: 0; // @presenter Spacing
|
|
1936
|
+
|
|
1937
|
+
--page-403-button-margin: 4em; // @presenter Spacing
|
|
1938
|
+
|
|
1939
|
+
// @tokens End
|
|
1883
1940
|
`;
|
|
1884
1941
|
exports.styles = (0, styled_components_1.css) `
|
|
1885
1942
|
:root {
|
|
@@ -1910,6 +1967,7 @@ exports.styles = (0, styled_components_1.css) `
|
|
|
1910
1967
|
${tile}
|
|
1911
1968
|
${loadProgressBar}
|
|
1912
1969
|
${apiLogsTable}
|
|
1970
|
+
${pages}
|
|
1913
1971
|
}
|
|
1914
1972
|
|
|
1915
1973
|
:root.dark {
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -37,4 +37,5 @@ __exportStar(require("./ColorModeSwitcher"), exports);
|
|
|
37
37
|
__exportStar(require("./Sidebar"), exports);
|
|
38
38
|
__exportStar(require("./types/config"), exports);
|
|
39
39
|
__exportStar(require("./config"), exports);
|
|
40
|
+
__exportStar(require("./Pages"), exports);
|
|
40
41
|
//# sourceMappingURL=index.js.map
|
package/lib/mocks/Link.js
CHANGED
|
@@ -21,7 +21,7 @@ const react_1 = __importDefault(require("react"));
|
|
|
21
21
|
function Link(props) {
|
|
22
22
|
const { active: _, httpVerb: _1, hasActiveSubItem: _2, routeSlug: _3, external: _4 } = props, filteredProps = __rest(props, ["active", "httpVerb", "hasActiveSubItem", "routeSlug", "external"]);
|
|
23
23
|
// We omit "active" property to avoid "Warning: Received `false` for a non-boolean attribute `active`."
|
|
24
|
-
return react_1.default.createElement("a", Object.assign({ href: filteredProps.to }, filteredProps));
|
|
24
|
+
return react_1.default.createElement("a", Object.assign({ href: filteredProps.to, ref: filteredProps.innerRef }, filteredProps));
|
|
25
25
|
}
|
|
26
26
|
exports.Link = Link;
|
|
27
27
|
//# sourceMappingURL=Link.js.map
|
package/lib/mocks/hooks/index.js
CHANGED
|
@@ -3,7 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.useSidebarSiblingsData = exports.useThemeConfig = void 0;
|
|
4
4
|
function useThemeConfig() {
|
|
5
5
|
return {
|
|
6
|
-
search: {
|
|
6
|
+
search: {
|
|
7
|
+
hide: false,
|
|
8
|
+
placement: 'navbar',
|
|
9
|
+
shortcuts: ['ctrl+f', 'cmd+k', '/'],
|
|
10
|
+
},
|
|
7
11
|
markdown: {
|
|
8
12
|
toc: { depth: 3, header: 'Table of contents', hide: false },
|
|
9
13
|
lastUpdatedBlock: { hide: false, format: 'timeago', locale: 'en-US' },
|
package/lib/mocks/search.js
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useFuseSearch = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
4
5
|
function useFuseSearch() {
|
|
6
|
+
const [query, setQuery] = (0, react_1.useState)('');
|
|
5
7
|
return {
|
|
6
|
-
query
|
|
7
|
-
setQuery
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
query,
|
|
9
|
+
setQuery,
|
|
10
|
+
items: [
|
|
11
|
+
{
|
|
12
|
+
id: '1',
|
|
13
|
+
url: '#someUrl1',
|
|
14
|
+
title: 'Some Dummy Result Item 1',
|
|
15
|
+
text: 'Some sample search text',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: '2',
|
|
19
|
+
url: '#someUrl2',
|
|
20
|
+
title: 'Some Dummy Result Item 2',
|
|
21
|
+
text: 'Some sample search text 2',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
11
24
|
};
|
|
12
25
|
}
|
|
13
26
|
exports.useFuseSearch = useFuseSearch;
|
package/lib/ui/darkColors.js
CHANGED
|
@@ -61,8 +61,13 @@ exports.darkMode = (0, styled_components_1.css) `
|
|
|
61
61
|
--copy-button-tooltip-background-color: var(--tooltip-background-color);
|
|
62
62
|
--tooltip-text-color: #fff;
|
|
63
63
|
--md-table-head-background-color: var(--color-secondary-300);
|
|
64
|
+
--md-tabs-hover-tab-border-color: var(--color-secondary-500);
|
|
64
65
|
--wide-tile-background-color: #000000;
|
|
65
66
|
--thin-tile-background-color: #000000;
|
|
67
|
+
--page-404-header-text-color: #fff;
|
|
68
|
+
--page-404-description-text-color: #fff;
|
|
69
|
+
--page-403-header-text-color: #fff;
|
|
70
|
+
--page-403-description-text-color: #fff;
|
|
66
71
|
|
|
67
72
|
background-color: var(--background-color);
|
|
68
73
|
color: var(--text-color);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Shared UI components lib",
|
|
5
5
|
"author": "team@redocly.com",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"prismjs": "^1.28.0",
|
|
40
40
|
"react": "^17.0.2",
|
|
41
41
|
"react-dom": "^17.0.2",
|
|
42
|
+
"react-router": "^5.3.0",
|
|
42
43
|
"react-router-dom": "^5.3.0",
|
|
43
44
|
"styled-components": "^5.3.6",
|
|
44
45
|
"styled-system": "^5.1.5",
|
|
@@ -65,16 +66,18 @@
|
|
|
65
66
|
"@types/jest": "^29.2.1",
|
|
66
67
|
"@types/jest-when": "^3.5.2",
|
|
67
68
|
"@types/lodash.throttle": "^4.1.7",
|
|
68
|
-
"@types/node": "^
|
|
69
|
+
"@types/node": "^18.11.18",
|
|
69
70
|
"@types/prismjs": "^1.26.0",
|
|
70
71
|
"@types/react": "^17.0.43",
|
|
71
72
|
"@types/react-dom": "^17.0.14",
|
|
72
|
-
"@types/react-router-dom": "^5.3.
|
|
73
|
+
"@types/react-router-dom": "^5.3.3",
|
|
74
|
+
"@types/react-router": "^5.1.20",
|
|
73
75
|
"@types/styled-components": "^5.1.26",
|
|
74
76
|
"@types/styled-system": "^5.1.13",
|
|
75
77
|
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
|
76
78
|
"@typescript-eslint/parser": "^5.23.0",
|
|
77
79
|
"chromatic": "^6.10.2",
|
|
80
|
+
"concurrently": "^7.4.0",
|
|
78
81
|
"esbuild": "^0.15.11",
|
|
79
82
|
"jest": "^29.2.2",
|
|
80
83
|
"jest-environment-jsdom": "^29.2.2",
|
|
@@ -83,6 +86,7 @@
|
|
|
83
86
|
"lodash.throttle": "^4.1.1",
|
|
84
87
|
"npm-run-all": "^4.1.5",
|
|
85
88
|
"react-refresh": "^0.14.0",
|
|
89
|
+
"react-router": "^5.3.0",
|
|
86
90
|
"react-router-dom": "^5.3.0",
|
|
87
91
|
"storybook-addon-pseudo-states": "^1.15.1",
|
|
88
92
|
"storybook-design-token": "^2.7.1",
|
|
@@ -95,10 +99,10 @@
|
|
|
95
99
|
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
|
96
100
|
"typescript": "^4.8.4",
|
|
97
101
|
"webpack": "^5.72.0",
|
|
98
|
-
"concurrently": "^7.4.0",
|
|
99
102
|
"zod": ">=3.19.1"
|
|
100
103
|
},
|
|
101
104
|
"dependencies": {
|
|
105
|
+
"hotkeys-js": "^3.10.1",
|
|
102
106
|
"timeago.js": "^4.0.2"
|
|
103
107
|
},
|
|
104
108
|
"nx": {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Button } from '@theme';
|
|
4
|
+
|
|
5
|
+
import type { CommentProps } from '@theme/Feedback';
|
|
6
|
+
|
|
7
|
+
export const Comment = ({ settings, onSubmit }: CommentProps): JSX.Element => {
|
|
8
|
+
const { label, submitText } = settings || {};
|
|
9
|
+
const [text, setText] = React.useState('');
|
|
10
|
+
const [submitValue, setSubmitValue] = React.useState('');
|
|
11
|
+
|
|
12
|
+
const send = () => {
|
|
13
|
+
if (!text) return;
|
|
14
|
+
setSubmitValue(text);
|
|
15
|
+
onSubmit({ comment: text });
|
|
16
|
+
};
|
|
17
|
+
const handleTextAreaChange = (e: any) => {
|
|
18
|
+
setText(e.target.value);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
if (submitValue) {
|
|
22
|
+
return (
|
|
23
|
+
<Wrapper>
|
|
24
|
+
<Label>{submitText || 'Thank you for helping improve our documentation!'}</Label>
|
|
25
|
+
</Wrapper>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Wrapper data-component-name="Feedback/Comment">
|
|
31
|
+
<Label>{label || 'Please share your feedback with us:'}</Label>
|
|
32
|
+
<TextArea rows={3} onChange={handleTextAreaChange} />
|
|
33
|
+
<SendButton onClick={send}>Send</SendButton>
|
|
34
|
+
</Wrapper>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const Wrapper = styled.div`
|
|
39
|
+
font-family: var(--font-family-base);
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
width: 100%;
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const Label = styled.h3`
|
|
46
|
+
margin-right: 15px;
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const TextArea = styled.textarea`
|
|
50
|
+
font-family: var(--font-family-base);
|
|
51
|
+
border: 1px solid #ccc;
|
|
52
|
+
border-radius: 5px;
|
|
53
|
+
color: black;
|
|
54
|
+
margin: 0 0 10px 0;
|
|
55
|
+
padding: 10px;
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const SendButton = styled(Button).attrs(() => ({
|
|
59
|
+
color: 'primary',
|
|
60
|
+
}))`
|
|
61
|
+
width: 100px;
|
|
62
|
+
margin-left: 0;
|
|
63
|
+
margin-right: 0;
|
|
64
|
+
`;
|
package/src/Feedback/Rating.tsx
CHANGED
|
@@ -1,36 +1,64 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
|
-
import type { RatingProps } from '@theme/Feedback';
|
|
4
|
+
import type { RatingProps, ReasonsProps } from '@theme/Feedback';
|
|
5
|
+
import { Comment, Reasons } from '@theme/Feedback';
|
|
5
6
|
|
|
6
7
|
type StarsProps = {
|
|
7
|
-
onSubmit:
|
|
8
|
+
onSubmit: (value: number) => void;
|
|
8
9
|
max: number;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
export const Rating = ({ settings, onSubmit }: RatingProps): JSX.Element => {
|
|
12
|
-
const {
|
|
13
|
-
|
|
13
|
+
const {
|
|
14
|
+
label,
|
|
15
|
+
max,
|
|
16
|
+
submitText,
|
|
17
|
+
comment: commentSettings,
|
|
18
|
+
reasons: reasonsSettings,
|
|
19
|
+
} = settings || {};
|
|
20
|
+
const [score, setScore] = React.useState(0);
|
|
21
|
+
const [comment, setComment] = React.useState('');
|
|
22
|
+
const [reasons, setReasons] = React.useState([] as ReasonsProps['settings']['items']);
|
|
14
23
|
|
|
15
|
-
if (
|
|
24
|
+
if (score && reasonsSettings?.enable && !reasons.length) {
|
|
25
|
+
const { label: reasonsLabel, items, multi } = reasonsSettings;
|
|
26
|
+
const buttonText = commentSettings?.enable ? 'Next' : 'Send';
|
|
16
27
|
return (
|
|
17
|
-
<
|
|
28
|
+
<Reasons
|
|
29
|
+
onSubmit={({ reasons }) => setReasons(reasons)}
|
|
30
|
+
settings={{ label: reasonsLabel, items, multi, buttonText }}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (score && commentSettings?.enable && !comment) {
|
|
36
|
+
return (
|
|
37
|
+
<Comment
|
|
38
|
+
onSubmit={({ comment }) => setComment(comment)}
|
|
39
|
+
settings={{ label: commentSettings.label }}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (score) {
|
|
45
|
+
onSubmit({
|
|
46
|
+
score,
|
|
47
|
+
comment,
|
|
48
|
+
reasons,
|
|
49
|
+
});
|
|
50
|
+
return (
|
|
51
|
+
<Wrapper>
|
|
18
52
|
<Label>{submitText || 'Thank you for helping improve our documentation!'}</Label>
|
|
19
|
-
</
|
|
53
|
+
</Wrapper>
|
|
20
54
|
);
|
|
21
55
|
}
|
|
22
56
|
|
|
23
57
|
return (
|
|
24
|
-
<
|
|
58
|
+
<Wrapper data-component-name="Feedback/Rating">
|
|
25
59
|
<Label>{label || 'How helpful was this page?'}</Label>
|
|
26
|
-
<Stars
|
|
27
|
-
|
|
28
|
-
onSubmit={(value: number) => {
|
|
29
|
-
setSubmitValue(value);
|
|
30
|
-
onSubmit(value);
|
|
31
|
-
}}
|
|
32
|
-
/>
|
|
33
|
-
</RatingWidget>
|
|
60
|
+
<Stars max={max || 5} onSubmit={setScore} />
|
|
61
|
+
</Wrapper>
|
|
34
62
|
);
|
|
35
63
|
};
|
|
36
64
|
|
|
@@ -64,7 +92,7 @@ const Stars = ({ max, onSubmit }: StarsProps): JSX.Element => {
|
|
|
64
92
|
return <>{stars}</>;
|
|
65
93
|
};
|
|
66
94
|
|
|
67
|
-
const
|
|
95
|
+
const Wrapper = styled.div`
|
|
68
96
|
display: flex;
|
|
69
97
|
align-items: center;
|
|
70
98
|
`;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Button } from '@theme';
|
|
4
|
+
|
|
5
|
+
import type { ReasonsProps } from '@theme/Feedback';
|
|
6
|
+
|
|
7
|
+
export const Reasons = ({ settings, onSubmit }: ReasonsProps): JSX.Element => {
|
|
8
|
+
const { label, multi, buttonText, items = [] } = settings;
|
|
9
|
+
const [checkedState, setCheckedState] = React.useState(new Array(items.length).fill(false));
|
|
10
|
+
|
|
11
|
+
if (!items.length) {
|
|
12
|
+
return <></>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const input_type = multi ? 'checkbox' : 'radio';
|
|
16
|
+
|
|
17
|
+
const handleOptionChange = (position: number) => {
|
|
18
|
+
const updatedCheckedState = multi
|
|
19
|
+
? checkedState.map((item, index) => (index === position ? !item : item))
|
|
20
|
+
: items.map((_, idx) => position === idx);
|
|
21
|
+
|
|
22
|
+
setCheckedState(updatedCheckedState);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const submitForm = () => {
|
|
26
|
+
onSubmit({ reasons: items.filter((_, index) => !!checkedState[index]) });
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Wrapper data-component-name="Feedback/Reasons">
|
|
31
|
+
<Label>{label || 'Which statement describes your thoughts about this page?'}</Label>
|
|
32
|
+
{items.map((reason, idx) => (
|
|
33
|
+
<OptionWrapper key={reason}>
|
|
34
|
+
<input
|
|
35
|
+
type={input_type}
|
|
36
|
+
value={reason}
|
|
37
|
+
checked={checkedState[idx]}
|
|
38
|
+
name="reasons"
|
|
39
|
+
onChange={() => handleOptionChange(idx)}
|
|
40
|
+
/>
|
|
41
|
+
<label id={reason} onClick={() => handleOptionChange(idx)}>
|
|
42
|
+
{reason}
|
|
43
|
+
</label>
|
|
44
|
+
</OptionWrapper>
|
|
45
|
+
))}
|
|
46
|
+
<SendButton onClick={submitForm}>{buttonText || 'Send'}</SendButton>
|
|
47
|
+
</Wrapper>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const Wrapper = styled.div`
|
|
52
|
+
font-family: var(--font-family-base);
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
const Label = styled.h3`
|
|
58
|
+
margin-right: 15px;
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
const SendButton = styled(Button).attrs(() => ({
|
|
62
|
+
color: 'primary',
|
|
63
|
+
}))`
|
|
64
|
+
width: 100px;
|
|
65
|
+
margin-left: 0;
|
|
66
|
+
margin-right: 0;
|
|
67
|
+
margin-top: 15px;
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
const OptionWrapper = styled.div`
|
|
71
|
+
margin: 5px 0;
|
|
72
|
+
display: flex;
|
|
73
|
+
input {
|
|
74
|
+
margin-right: 10px;
|
|
75
|
+
cursor: pointer;
|
|
76
|
+
}
|
|
77
|
+
label {
|
|
78
|
+
font-size: 14px;
|
|
79
|
+
cursor: pointer;
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
@@ -1,19 +1,58 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
|
-
import type { SentimentProps } from '@theme/Feedback';
|
|
4
|
+
import type { SentimentProps, ReasonsProps } from '@theme/Feedback';
|
|
5
|
+
import { Comment, Reasons } from '@theme/Feedback';
|
|
5
6
|
|
|
6
7
|
import { ThumbUp, ThumbDown } from './Thumbs';
|
|
7
8
|
|
|
8
9
|
export const Sentiment = ({ settings, onSubmit }: SentimentProps): JSX.Element => {
|
|
9
|
-
const { label } = settings || {};
|
|
10
|
+
const { label, submitText, comment: commentSettings, reasons: reasonsSettings } = settings || {};
|
|
11
|
+
const [score, setScore] = React.useState(0);
|
|
12
|
+
const [comment, setComment] = React.useState('');
|
|
13
|
+
const [reasons, setReasons] = React.useState([] as ReasonsProps['settings']['items']);
|
|
14
|
+
|
|
15
|
+
if (score && reasonsSettings?.enable && !reasons.length) {
|
|
16
|
+
const { label: reasonsLabel, items, multi } = reasonsSettings;
|
|
17
|
+
const buttonText = commentSettings?.enable ? 'Next' : 'Send';
|
|
18
|
+
return (
|
|
19
|
+
<Reasons
|
|
20
|
+
onSubmit={({ reasons }) => setReasons(reasons)}
|
|
21
|
+
settings={{ label: reasonsLabel, items, multi, buttonText }}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (score && commentSettings?.enable && !comment) {
|
|
27
|
+
const commentLabel =
|
|
28
|
+
score === 1
|
|
29
|
+
? commentSettings.likeLabel || 'What was most helpful?'
|
|
30
|
+
: commentSettings.dislikeLabel || 'What can we improve?';
|
|
31
|
+
return (
|
|
32
|
+
<Comment onSubmit={({ comment }) => setComment(comment)} settings={{ label: commentLabel }} />
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (score) {
|
|
37
|
+
onSubmit({
|
|
38
|
+
score,
|
|
39
|
+
comment,
|
|
40
|
+
reasons,
|
|
41
|
+
});
|
|
42
|
+
return (
|
|
43
|
+
<Wrapper>
|
|
44
|
+
<Label>{submitText || 'Thank you for helping improve our documentation!'}</Label>
|
|
45
|
+
</Wrapper>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
10
49
|
return (
|
|
11
|
-
<Wrapper>
|
|
50
|
+
<Wrapper data-component-name="Feedback/Sentiment">
|
|
12
51
|
<Label>{label || 'Was this page helpful?'}</Label>
|
|
13
|
-
<Vote onClick={() =>
|
|
52
|
+
<Vote onClick={() => setScore(1)}>
|
|
14
53
|
<ThumbUp text="Yes" />
|
|
15
54
|
</Vote>
|
|
16
|
-
<Vote onClick={() =>
|
|
55
|
+
<Vote onClick={() => setScore(-1)}>
|
|
17
56
|
<ThumbDown />
|
|
18
57
|
</Vote>
|
|
19
58
|
</Wrapper>
|