@redocly/theme 0.6.4 → 0.7.0

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.
Files changed (86) hide show
  1. package/lib/Feedback/Comment.d.ts +3 -0
  2. package/lib/Feedback/Comment.js +80 -0
  3. package/lib/Feedback/Rating.d.ts +3 -0
  4. package/lib/Feedback/Rating.js +82 -0
  5. package/lib/Feedback/Reasons.d.ts +3 -0
  6. package/lib/Feedback/Reasons.js +85 -0
  7. package/lib/Feedback/Sentiment.d.ts +3 -0
  8. package/lib/Feedback/Sentiment.js +79 -0
  9. package/lib/Feedback/Thumbs.d.ts +7 -0
  10. package/lib/Feedback/Thumbs.js +79 -0
  11. package/lib/Feedback/index.d.ts +5 -0
  12. package/lib/Feedback/index.js +27 -0
  13. package/lib/Feedback/types.d.ts +71 -0
  14. package/lib/Feedback/types.js +3 -0
  15. package/lib/Markdown/MarkdownLayout.d.ts +2 -1
  16. package/lib/Markdown/MarkdownLayout.js +8 -2
  17. package/lib/Markdown/Tabs/Tab.js +11 -5
  18. package/lib/Markdown/Tabs/Tabs.js +14 -5
  19. package/lib/Navbar/Navbar.js +6 -3
  20. package/lib/Pages/Forbidden.d.ts +2 -0
  21. package/lib/Pages/Forbidden.js +39 -0
  22. package/lib/Pages/NotFound.d.ts +2 -0
  23. package/lib/Pages/NotFound.js +39 -0
  24. package/lib/Pages/index.d.ts +1 -0
  25. package/lib/Pages/index.js +18 -0
  26. package/lib/Profile/LoginLink.d.ts +5 -0
  27. package/lib/Profile/LoginLink.js +30 -0
  28. package/lib/Profile/Profile.js +3 -1
  29. package/lib/Profile/UserProfile.d.ts +13 -0
  30. package/lib/Profile/UserProfile.js +82 -0
  31. package/lib/Profile/index.d.ts +4 -0
  32. package/lib/Profile/index.js +5 -1
  33. package/lib/Search/Autocomplete.d.ts +4 -1
  34. package/lib/Search/Autocomplete.js +19 -3
  35. package/lib/Search/ClearIcon.js +1 -1
  36. package/lib/Search/Input.js +1 -1
  37. package/lib/Search/Search.js +6 -1
  38. package/lib/Search/SearchIcon.js +1 -1
  39. package/lib/Search/ShortcutKey.d.ts +7 -0
  40. package/lib/Search/ShortcutKey.js +35 -0
  41. package/lib/config.d.ts +61 -0
  42. package/lib/config.js +19 -0
  43. package/lib/globalStyle.js +62 -0
  44. package/lib/index.d.ts +2 -0
  45. package/lib/index.js +2 -0
  46. package/lib/mocks/Link.js +1 -1
  47. package/lib/mocks/hooks/index.js +11 -1
  48. package/lib/mocks/search.js +18 -5
  49. package/lib/ui/Box.d.ts +2 -2
  50. package/lib/ui/Box.js +1 -0
  51. package/lib/ui/darkColors.js +5 -0
  52. package/package.json +9 -5
  53. package/src/Feedback/Comment.tsx +64 -0
  54. package/src/Feedback/Rating.tsx +107 -0
  55. package/src/Feedback/Reasons.tsx +81 -0
  56. package/src/Feedback/Sentiment.tsx +75 -0
  57. package/src/Feedback/Thumbs.tsx +116 -0
  58. package/src/Feedback/index.ts +5 -0
  59. package/src/Feedback/types.ts +63 -0
  60. package/src/Markdown/MarkdownLayout.tsx +10 -1
  61. package/src/Markdown/Tabs/Tab.tsx +11 -5
  62. package/src/Markdown/Tabs/Tabs.tsx +14 -5
  63. package/src/Navbar/Navbar.tsx +8 -3
  64. package/src/Pages/Forbidden.tsx +42 -0
  65. package/src/Pages/NotFound.tsx +42 -0
  66. package/src/Pages/index.ts +1 -0
  67. package/src/Profile/LoginLink.tsx +29 -0
  68. package/src/Profile/Profile.tsx +3 -1
  69. package/src/Profile/UserProfile.tsx +101 -0
  70. package/src/Profile/index.ts +4 -0
  71. package/src/Search/Autocomplete.tsx +26 -2
  72. package/src/Search/ClearIcon.tsx +1 -1
  73. package/src/Search/Input.tsx +1 -1
  74. package/src/Search/Search.tsx +3 -0
  75. package/src/Search/SearchIcon.tsx +1 -1
  76. package/src/Search/ShortcutKey.tsx +35 -0
  77. package/src/config.ts +23 -0
  78. package/src/globalStyle.ts +64 -0
  79. package/src/index.ts +2 -0
  80. package/src/mocks/Link.tsx +2 -1
  81. package/src/mocks/hooks/index.ts +11 -1
  82. package/src/mocks/search.ts +20 -5
  83. package/src/settings.yaml +2 -0
  84. package/src/types/portal/index.d.ts +1 -1
  85. package/src/ui/Box.tsx +5 -2
  86. package/src/ui/darkColors.tsx +5 -0
@@ -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
  /**
@@ -1878,6 +1888,56 @@ const tile = (0, styled_components_1.css) `
1878
1888
  --wide-tile-background-color: var(--color-secondary-50);
1879
1889
  --thin-tile-background-color: var(--color-secondary-50);
1880
1890
  `;
1891
+ const apiLogsTable = (0, styled_components_1.css) `
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
1940
+ `;
1881
1941
  exports.styles = (0, styled_components_1.css) `
1882
1942
  :root {
1883
1943
  ${baseColors}
@@ -1906,6 +1966,8 @@ exports.styles = (0, styled_components_1.css) `
1906
1966
  ${lastUpdated}
1907
1967
  ${tile}
1908
1968
  ${loadProgressBar}
1969
+ ${apiLogsTable}
1970
+ ${pages}
1909
1971
  }
1910
1972
 
1911
1973
  :root.dark {
package/lib/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export * from './Tooltip';
10
10
  export * from './SourceCode';
11
11
  export * from './Panel';
12
12
  export * from './icons';
13
+ export * from './Feedback';
13
14
  export * from './hooks';
14
15
  export * from './utils';
15
16
  export * from './globalStyle';
@@ -20,3 +21,4 @@ export * from './ColorModeSwitcher';
20
21
  export * from './Sidebar';
21
22
  export * from './types/config';
22
23
  export * from './config';
24
+ export * from './Pages';
package/lib/index.js CHANGED
@@ -26,6 +26,7 @@ __exportStar(require("./Tooltip"), exports);
26
26
  __exportStar(require("./SourceCode"), exports);
27
27
  __exportStar(require("./Panel"), exports);
28
28
  __exportStar(require("./icons"), exports);
29
+ __exportStar(require("./Feedback"), exports);
29
30
  __exportStar(require("./hooks"), exports);
30
31
  __exportStar(require("./utils"), exports);
31
32
  __exportStar(require("./globalStyle"), exports);
@@ -36,4 +37,5 @@ __exportStar(require("./ColorModeSwitcher"), exports);
36
37
  __exportStar(require("./Sidebar"), exports);
37
38
  __exportStar(require("./types/config"), exports);
38
39
  __exportStar(require("./config"), exports);
40
+ __exportStar(require("./Pages"), exports);
39
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
@@ -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: { hide: false, placement: 'navbar' },
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' },
@@ -27,6 +31,12 @@ function useThemeConfig() {
27
31
  colorMode: {
28
32
  modes: ['light', 'dark'],
29
33
  },
34
+ feedback: {
35
+ type: 'sentiment',
36
+ },
37
+ userProfile: {
38
+ hide: false,
39
+ },
30
40
  };
31
41
  }
32
42
  exports.useThemeConfig = useThemeConfig;
@@ -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(val) {
8
- this.query = val;
9
- },
10
- items: [],
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/Box.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { SpaceProps, LayoutProps, TextAlignProps, ColorProps, PositionProps, FlexProps, BordersProps } from 'styled-system';
2
- export interface BoxProps extends SpaceProps, LayoutProps, PositionProps, FlexProps, TextAlignProps, ColorProps, BordersProps {
1
+ import type { SpaceProps, LayoutProps, TextAlignProps, ColorProps, PositionProps, FlexProps, BordersProps, GridProps } from 'styled-system';
2
+ export interface BoxProps extends SpaceProps, LayoutProps, PositionProps, FlexProps, TextAlignProps, ColorProps, BordersProps, GridProps {
3
3
  }
4
4
  export declare const Box: import("styled-components").StyledComponent<"div", any, {
5
5
  'data-component-name': string;
package/lib/ui/Box.js CHANGED
@@ -17,5 +17,6 @@ exports.Box = styled_components_1.default.div.attrs(() => ({
17
17
  ${styled_system_1.textAlign}
18
18
  ${styled_system_1.color}
19
19
  ${styled_system_1.border}
20
+ ${styled_system_1.grid}
20
21
  `;
21
22
  //# sourceMappingURL=Box.js.map
@@ -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,7 +1,7 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.6.4",
4
- "description": "Shared UI components",
3
+ "version": "0.7.0",
4
+ "description": "Shared UI components lib",
5
5
  "author": "team@redocly.com",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "main": "lib/index.js",
@@ -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": "^16.11.26",
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.1",
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
+ `;
@@ -0,0 +1,107 @@
1
+ import * as React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { RatingProps, ReasonsProps } from '@theme/Feedback';
5
+ import { Comment, Reasons } from '@theme/Feedback';
6
+
7
+ type StarsProps = {
8
+ onSubmit: (value: number) => void;
9
+ max: number;
10
+ };
11
+
12
+ export const Rating = ({ settings, onSubmit }: RatingProps): JSX.Element => {
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']);
23
+
24
+ if (score && reasonsSettings?.enable && !reasons.length) {
25
+ const { label: reasonsLabel, items, multi } = reasonsSettings;
26
+ const buttonText = commentSettings?.enable ? 'Next' : 'Send';
27
+ return (
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>
52
+ <Label>{submitText || 'Thank you for helping improve our documentation!'}</Label>
53
+ </Wrapper>
54
+ );
55
+ }
56
+
57
+ return (
58
+ <Wrapper data-component-name="Feedback/Rating">
59
+ <Label>{label || 'How helpful was this page?'}</Label>
60
+ <Stars max={max || 5} onSubmit={setScore} />
61
+ </Wrapper>
62
+ );
63
+ };
64
+
65
+ const Stars = ({ max, onSubmit }: StarsProps): JSX.Element => {
66
+ const [hovered, setHovered] = React.useState(0);
67
+ const stars: JSX.Element[] = [];
68
+
69
+ for (let index = 1; index <= max; index++) {
70
+ stars.push(
71
+ <Star
72
+ key={index}
73
+ onClick={() => onSubmit(index)}
74
+ onMouseOver={() => setHovered(index)}
75
+ onMouseLeave={() => setHovered(0)}
76
+ >
77
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
78
+ {hovered < index ? (
79
+ <g fill="#e8c002">
80
+ <path d="M20 7h-7L10 .5 7 7H0l5.46 5.47-1.64 7 6.18-3.7 6.18 3.73-1.63-7zm-10 6.9-3.76 2.27 1-4.28L3.5 8.5h4.61L10 4.6l1.9 3.9h4.6l-3.73 3.4 1 4.28z" />
81
+ </g>
82
+ ) : (
83
+ <g fill="#e8c002">
84
+ <path d="M20 7h-7L10 .5 7 7H0l5.46 5.47-1.64 7 6.18-3.7 6.18 3.73-1.63-7z" />
85
+ </g>
86
+ )}
87
+ </svg>
88
+ </Star>,
89
+ );
90
+ }
91
+
92
+ return <>{stars}</>;
93
+ };
94
+
95
+ const Wrapper = styled.div`
96
+ display: flex;
97
+ align-items: center;
98
+ `;
99
+
100
+ const Star = styled.span`
101
+ cursor: pointer;
102
+ `;
103
+
104
+ const Label = styled.h3`
105
+ margin-right: 15px;
106
+ font-family: var(--font-family-base);
107
+ `;
@@ -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
+ `;
@@ -0,0 +1,75 @@
1
+ import * as React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { SentimentProps, ReasonsProps } from '@theme/Feedback';
5
+ import { Comment, Reasons } from '@theme/Feedback';
6
+
7
+ import { ThumbUp, ThumbDown } from './Thumbs';
8
+
9
+ export const Sentiment = ({ settings, onSubmit }: SentimentProps): JSX.Element => {
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
+
49
+ return (
50
+ <Wrapper data-component-name="Feedback/Sentiment">
51
+ <Label>{label || 'Was this page helpful?'}</Label>
52
+ <Vote onClick={() => setScore(1)}>
53
+ <ThumbUp text="Yes" />
54
+ </Vote>
55
+ <Vote onClick={() => setScore(-1)}>
56
+ <ThumbDown />
57
+ </Vote>
58
+ </Wrapper>
59
+ );
60
+ };
61
+
62
+ const Wrapper = styled.div`
63
+ font-family: var(--font-family-base);
64
+ display: flex;
65
+ align-items: center;
66
+ `;
67
+
68
+ const Label = styled.h3`
69
+ margin-right: 15px;
70
+ `;
71
+
72
+ const Vote = styled.div`
73
+ cursor: pointer;
74
+ margin: 0 10px;
75
+ `;