@redocly/theme 0.6.4 → 0.6.5

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 (47) hide show
  1. package/lib/Feedback/Rating.d.ts +3 -0
  2. package/lib/Feedback/Rating.js +69 -0
  3. package/lib/Feedback/Sentiment.d.ts +3 -0
  4. package/lib/Feedback/Sentiment.js +55 -0
  5. package/lib/Feedback/Thumbs.d.ts +7 -0
  6. package/lib/Feedback/Thumbs.js +79 -0
  7. package/lib/Feedback/index.d.ts +3 -0
  8. package/lib/Feedback/index.js +23 -0
  9. package/lib/Feedback/types.d.ts +26 -0
  10. package/lib/Feedback/types.js +3 -0
  11. package/lib/Markdown/MarkdownLayout.d.ts +2 -1
  12. package/lib/Markdown/MarkdownLayout.js +8 -2
  13. package/lib/Navbar/Navbar.js +3 -2
  14. package/lib/Profile/LoginLink.d.ts +5 -0
  15. package/lib/Profile/LoginLink.js +30 -0
  16. package/lib/Profile/Profile.js +3 -1
  17. package/lib/Profile/UserProfile.d.ts +13 -0
  18. package/lib/Profile/UserProfile.js +82 -0
  19. package/lib/Profile/index.d.ts +4 -0
  20. package/lib/Profile/index.js +5 -1
  21. package/lib/config.d.ts +53 -0
  22. package/lib/config.js +18 -0
  23. package/lib/globalStyle.js +4 -0
  24. package/lib/index.d.ts +1 -0
  25. package/lib/index.js +1 -0
  26. package/lib/mocks/hooks/index.js +6 -0
  27. package/lib/ui/Box.d.ts +2 -2
  28. package/lib/ui/Box.js +1 -0
  29. package/package.json +2 -2
  30. package/src/Feedback/Rating.tsx +79 -0
  31. package/src/Feedback/Sentiment.tsx +36 -0
  32. package/src/Feedback/Thumbs.tsx +116 -0
  33. package/src/Feedback/index.ts +3 -0
  34. package/src/Feedback/types.ts +29 -0
  35. package/src/Markdown/MarkdownLayout.tsx +10 -1
  36. package/src/Navbar/Navbar.tsx +3 -2
  37. package/src/Profile/LoginLink.tsx +29 -0
  38. package/src/Profile/Profile.tsx +3 -1
  39. package/src/Profile/UserProfile.tsx +101 -0
  40. package/src/Profile/index.ts +4 -0
  41. package/src/config.ts +19 -0
  42. package/src/globalStyle.ts +5 -0
  43. package/src/index.ts +1 -0
  44. package/src/mocks/hooks/index.ts +6 -0
  45. package/src/settings.yaml +2 -0
  46. package/src/types/portal/index.d.ts +1 -1
  47. package/src/ui/Box.tsx +5 -2
@@ -1878,6 +1878,9 @@ const tile = (0, styled_components_1.css) `
1878
1878
  --wide-tile-background-color: var(--color-secondary-50);
1879
1879
  --thin-tile-background-color: var(--color-secondary-50);
1880
1880
  `;
1881
+ const apiLogsTable = (0, styled_components_1.css) `
1882
+ --api-logs-row-hover-background-color: var(--color-secondary-300)
1883
+ `;
1881
1884
  exports.styles = (0, styled_components_1.css) `
1882
1885
  :root {
1883
1886
  ${baseColors}
@@ -1906,6 +1909,7 @@ exports.styles = (0, styled_components_1.css) `
1906
1909
  ${lastUpdated}
1907
1910
  ${tile}
1908
1911
  ${loadProgressBar}
1912
+ ${apiLogsTable}
1909
1913
  }
1910
1914
 
1911
1915
  :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';
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);
@@ -27,6 +27,12 @@ function useThemeConfig() {
27
27
  colorMode: {
28
28
  modes: ['light', 'dark'],
29
29
  },
30
+ feedback: {
31
+ type: 'sentiment',
32
+ },
33
+ userProfile: {
34
+ hide: false,
35
+ },
30
36
  };
31
37
  }
32
38
  exports.useThemeConfig = useThemeConfig;
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
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.6.5",
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",
@@ -0,0 +1,79 @@
1
+ import * as React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { RatingProps } from '@theme/Feedback';
5
+
6
+ type StarsProps = {
7
+ onSubmit: RatingProps['onSubmit'];
8
+ max: number;
9
+ };
10
+
11
+ export const Rating = ({ settings, onSubmit }: RatingProps): JSX.Element => {
12
+ const { label, max, submitText } = settings || {};
13
+ const [submitValue, setSubmitValue] = React.useState(0);
14
+
15
+ if (submitValue) {
16
+ return (
17
+ <RatingWidget>
18
+ <Label>{submitText || 'Thank you for helping improve our documentation!'}</Label>
19
+ </RatingWidget>
20
+ );
21
+ }
22
+
23
+ return (
24
+ <RatingWidget>
25
+ <Label>{label || 'How helpful was this page?'}</Label>
26
+ <Stars
27
+ max={max || 5}
28
+ onSubmit={(value: number) => {
29
+ setSubmitValue(value);
30
+ onSubmit(value);
31
+ }}
32
+ />
33
+ </RatingWidget>
34
+ );
35
+ };
36
+
37
+ const Stars = ({ max, onSubmit }: StarsProps): JSX.Element => {
38
+ const [hovered, setHovered] = React.useState(0);
39
+ const stars: JSX.Element[] = [];
40
+
41
+ for (let index = 1; index <= max; index++) {
42
+ stars.push(
43
+ <Star
44
+ key={index}
45
+ onClick={() => onSubmit(index)}
46
+ onMouseOver={() => setHovered(index)}
47
+ onMouseLeave={() => setHovered(0)}
48
+ >
49
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
50
+ {hovered < index ? (
51
+ <g fill="#e8c002">
52
+ <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" />
53
+ </g>
54
+ ) : (
55
+ <g fill="#e8c002">
56
+ <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" />
57
+ </g>
58
+ )}
59
+ </svg>
60
+ </Star>,
61
+ );
62
+ }
63
+
64
+ return <>{stars}</>;
65
+ };
66
+
67
+ const RatingWidget = styled.div`
68
+ display: flex;
69
+ align-items: center;
70
+ `;
71
+
72
+ const Star = styled.span`
73
+ cursor: pointer;
74
+ `;
75
+
76
+ const Label = styled.h3`
77
+ margin-right: 15px;
78
+ font-family: var(--font-family-base);
79
+ `;
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { SentimentProps } from '@theme/Feedback';
5
+
6
+ import { ThumbUp, ThumbDown } from './Thumbs';
7
+
8
+ export const Sentiment = ({ settings, onSubmit }: SentimentProps): JSX.Element => {
9
+ const { label } = settings || {};
10
+ return (
11
+ <Wrapper>
12
+ <Label>{label || 'Was this page helpful?'}</Label>
13
+ <Vote onClick={() => onSubmit(1)}>
14
+ <ThumbUp text="Yes" />
15
+ </Vote>
16
+ <Vote onClick={() => onSubmit(-1)}>
17
+ <ThumbDown />
18
+ </Vote>
19
+ </Wrapper>
20
+ );
21
+ };
22
+
23
+ const Wrapper = styled.div`
24
+ font-family: var(--font-family-base);
25
+ display: flex;
26
+ align-items: center;
27
+ `;
28
+
29
+ const Label = styled.h3`
30
+ margin-right: 15px;
31
+ `;
32
+
33
+ const Vote = styled.div`
34
+ cursor: pointer;
35
+ margin: 0 10px;
36
+ `;
@@ -0,0 +1,116 @@
1
+ import * as React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ export const ThumbUp = ({ text }: { text?: string }) => (
5
+ <Wrapper style={{ alignItems: 'normal' }}>
6
+ <Icon>
7
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 512 512">
8
+ <g>
9
+ <g>
10
+ <g>
11
+ <path
12
+ d="M495.736,290.773C509.397,282.317,512,269.397,512,260.796c0-22.4-18.253-47.462-42.667-47.462H349.918
13
+ c-4.284-0.051-25.651-1.51-25.651-25.6c0-4.71-3.814-8.533-8.533-8.533s-8.533,3.823-8.533,8.533
14
+ c0,33.749,27.913,42.667,42.667,42.667h119.467c14.182,0,25.6,16.631,25.6,30.396c0,4.437,0,17.946-26.53,20.855
15
+ c-4.506,0.495-7.834,4.42-7.586,8.951c0.239,4.523,3.985,8.064,8.516,8.064c14.114,0,25.6,11.486,25.6,25.6
16
+ s-11.486,25.6-25.6,25.6h-17.067c-4.719,0-8.533,3.823-8.533,8.533s3.814,8.533,8.533,8.533c14.114,0,25.6,11.486,25.6,25.6
17
+ s-11.486,25.6-25.6,25.6h-25.6c-4.719,0-8.533,3.823-8.533,8.533s3.814,8.533,8.533,8.533c8.934,0,17.067,8.132,17.067,17.067
18
+ c0,8.61-8.448,17.067-17.067,17.067h-128c-35.627,0-48.444-7.074-63.292-15.258c-12.553-6.921-26.786-14.763-54.963-18.79
19
+ c-4.668-0.674-8.994,2.577-9.66,7.236c-0.666,4.668,2.569,8.994,7.236,9.66c25.105,3.584,37.325,10.325,49.152,16.845
20
+ c15.497,8.542,31.505,17.374,71.526,17.374h128c17.869,0,34.133-16.273,34.133-34.133c0-6.229-1.775-12.134-4.83-17.229
21
+ c21.794-1.877,38.963-20.224,38.963-42.505c0-10.829-4.062-20.736-10.735-28.271C500.42,358.212,512,342.571,512,324.267
22
+ C512,310.699,505.634,298.59,495.736,290.773z"
23
+ />
24
+ <path
25
+ d="M76.8,443.733c9.412,0,17.067-7.654,17.067-17.067S86.212,409.6,76.8,409.6c-9.412,0-17.067,7.654-17.067,17.067
26
+ S67.388,443.733,76.8,443.733z"
27
+ />
28
+ <path
29
+ d="M179.2,247.467c25.353,0,57.429-28.297,74.3-45.167c36.634-36.634,36.634-82.167,36.634-151.1
30
+ c0-5.342,3.191-8.533,8.533-8.533c29.508,0,42.667,13.158,42.667,42.667v102.4c0,4.71,3.814,8.533,8.533,8.533
31
+ s8.533-3.823,8.533-8.533v-102.4c0-39.083-20.659-59.733-59.733-59.733c-14.831,0-25.6,10.769-25.6,25.6
32
+ c0,66.978,0,107.401-31.633,139.034C216.661,215.006,192.811,230.4,179.2,230.4c-4.719,0-8.533,3.823-8.533,8.533
33
+ S174.481,247.467,179.2,247.467z"
34
+ />
35
+ <path
36
+ d="M145.067,213.333H8.533c-4.719,0-8.533,3.823-8.533,8.533v256c0,4.71,3.814,8.533,8.533,8.533h136.533
37
+ c4.719,0,8.533-3.823,8.533-8.533v-256C153.6,217.156,149.786,213.333,145.067,213.333z M136.533,469.333H17.067V230.4h119.467
38
+ V469.333z"
39
+ />
40
+ </g>
41
+ </g>
42
+ </g>
43
+ </svg>
44
+ </Icon>
45
+ <span>{text || 'Yes'}</span>
46
+ </Wrapper>
47
+ );
48
+
49
+ export const ThumbDown = ({ text }: { text?: string }) => (
50
+ <Wrapper style={{ alignItems: 'end' }}>
51
+ <Icon>
52
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 512 512">
53
+ <g>
54
+ <g>
55
+ <g>
56
+ <path
57
+ d="M76.8,247.467c9.412,0,17.067-7.654,17.067-17.067c0-9.412-7.654-17.067-17.067-17.067
58
+ c-9.412,0-17.067,7.654-17.067,17.067C59.733,239.812,67.388,247.467,76.8,247.467z"
59
+ />
60
+ <path
61
+ d="M495.736,221.227C505.634,213.41,512,201.301,512,187.733c0-18.295-11.58-33.946-27.802-39.996
62
+ c6.673-7.535,10.735-17.434,10.735-28.271c0-22.281-17.169-40.627-38.963-42.505c3.055-5.094,4.83-10.999,4.83-17.229
63
+ c0-17.86-16.265-34.133-34.133-34.133h-128c-40.021,0-56.03,8.832-71.526,17.374c-11.827,6.519-24.047,13.261-49.152,16.845
64
+ c-4.668,0.666-7.902,4.992-7.236,9.66c0.666,4.659,4.949,7.885,9.66,7.236c28.177-4.028,42.411-11.87,54.963-18.79
65
+ c14.848-8.183,27.665-15.258,63.292-15.258h128c8.619,0,17.067,8.456,17.067,17.067c0,8.934-8.132,17.067-17.067,17.067
66
+ c-4.719,0-8.533,3.823-8.533,8.533s3.814,8.533,8.533,8.533h25.6c14.114,0,25.6,11.486,25.6,25.6s-11.486,25.6-25.6,25.6
67
+ c-4.719,0-8.533,3.823-8.533,8.533c0,4.71,3.814,8.533,8.533,8.533h17.067c14.114,0,25.6,11.486,25.6,25.6
68
+ s-11.486,25.6-25.6,25.6c-4.531,0-8.277,3.541-8.516,8.064c-0.247,4.531,3.081,8.457,7.586,8.951
69
+ c26.53,2.91,26.53,16.418,26.53,20.847c0,13.773-11.418,30.404-25.6,30.404H349.867c-14.763,0-42.667,8.917-42.667,42.667
70
+ c0,4.71,3.814,8.533,8.533,8.533s8.533-3.823,8.533-8.533c0-24.09,21.367-25.549,25.6-25.6h119.467
71
+ c24.414,0,42.667-25.054,42.667-47.471C512,242.603,509.397,229.683,495.736,221.227z"
72
+ />
73
+ <path
74
+ d="M349.867,315.733c-4.719,0-8.533,3.823-8.533,8.533v102.4c0,29.508-13.158,42.667-42.667,42.667
75
+ c-5.342,0-8.533-3.192-8.533-8.533c0-68.932,0-114.466-36.634-151.1c-16.87-16.87-48.947-45.167-74.3-45.167
76
+ c-4.719,0-8.533,3.823-8.533,8.533s3.814,8.533,8.533,8.533c13.611,0,37.461,15.394,62.234,40.166
77
+ c31.633,31.633,31.633,72.055,31.633,139.034c0,14.831,10.769,25.6,25.6,25.6c39.074,0,59.733-20.651,59.733-59.733v-102.4
78
+ C358.4,319.556,354.586,315.733,349.867,315.733z"
79
+ />
80
+ <path
81
+ d="M145.067,25.6H8.533C3.814,25.6,0,29.423,0,34.133v256c0,4.71,3.814,8.533,8.533,8.533h136.533
82
+ c4.719,0,8.533-3.823,8.533-8.533v-256C153.6,29.423,149.786,25.6,145.067,25.6z M136.533,281.6H17.067V42.667h119.467V281.6z"
83
+ />
84
+ </g>
85
+ </g>
86
+ </g>
87
+ </svg>
88
+ </Icon>
89
+ <span>{text || 'No'}</span>
90
+ </Wrapper>
91
+ );
92
+
93
+ const Wrapper = styled.div`
94
+ font-family: var(--font-family-base);
95
+ display: flex;
96
+ color: var(--color-primary-500);
97
+ &:hover {
98
+ color: var(--color-primary-600);
99
+ svg {
100
+ > g {
101
+ fill: var(--color-primary-600);
102
+ }
103
+ }
104
+ }
105
+ `;
106
+
107
+ const Icon = styled.div`
108
+ width: 18px;
109
+ height: 18px;
110
+ margin-right: 5px;
111
+ > svg {
112
+ > g {
113
+ fill: var(--color-primary-500);
114
+ }
115
+ }
116
+ `;
@@ -0,0 +1,3 @@
1
+ export { Rating } from './Rating';
2
+ export { Sentiment } from './Sentiment';
3
+ export * from './types';
@@ -0,0 +1,29 @@
1
+ export type RatingProps = {
2
+ onSubmit: (value: number) => void;
3
+ settings?: {
4
+ label?: string;
5
+ max?: number;
6
+ submitText?: string;
7
+ };
8
+ };
9
+
10
+ export type SentimentProps = {
11
+ onSubmit: (value: -1 | 1) => void;
12
+ settings?: {
13
+ label?: string;
14
+ };
15
+ };
16
+
17
+ export type CommentProps = {
18
+ onSubmit: (value: string) => void;
19
+ settings?: {
20
+ label?: string;
21
+ };
22
+ };
23
+
24
+ export type ProblemProps = {
25
+ onSubmit: (value: string, location: string) => void; // TODO: maybe we don't need location here
26
+ settings?: {
27
+ label?: string;
28
+ };
29
+ };
@@ -11,6 +11,7 @@ import { useThemeConfig } from '@theme/hooks';
11
11
  type MarkdownLayoutProps = {
12
12
  tableOfContent: React.ReactNode;
13
13
  markdownWrapper: React.ReactNode;
14
+ feedback: React.ReactNode;
14
15
  editPage?: {
15
16
  to: string;
16
17
  text: string;
@@ -23,13 +24,14 @@ type MarkdownLayoutProps = {
23
24
  export function MarkdownLayout({
24
25
  tableOfContent,
25
26
  markdownWrapper,
27
+ feedback,
26
28
  editPage,
27
29
  lastModified,
28
30
  }: MarkdownLayoutProps): JSX.Element {
29
31
  const { markdown } = useThemeConfig();
30
32
  const { editPage: themeEditPage } = markdown || {};
31
33
 
32
- const mergedConf = editPage ? { ...editPage, ...themeEditPage } : undefined;
34
+ const mergedConf = editPage ? { ...themeEditPage, ...editPage } : undefined;
33
35
 
34
36
  return (
35
37
  <PageWrapper data-component-name="Markdown/MarkdownLayout">
@@ -41,6 +43,7 @@ export function MarkdownLayout({
41
43
  )}
42
44
  </LayoutTop>
43
45
  {markdownWrapper}
46
+ <LayoutBottom>{feedback}</LayoutBottom>
44
47
  <PageNavigation />
45
48
  </ContainerWrapper>
46
49
  {tableOfContent}
@@ -53,3 +56,9 @@ const LayoutTop = styled.div`
53
56
  justify-content: space-between;
54
57
  flex-flow: row nowrap;
55
58
  `;
59
+
60
+ const LayoutBottom = styled(LayoutTop)`
61
+ > * {
62
+ margin: 25px 5px;
63
+ }
64
+ `;
@@ -18,9 +18,10 @@ interface NavbarProps {
18
18
 
19
19
  export function Navbar({ menu, logo, search, profile }: NavbarProps): JSX.Element | null {
20
20
  const [isOpen, setIsOpen] = useMobileMenu(false);
21
- const { search: searchSettings, navbar } = useThemeConfig();
21
+ const { search: searchSettings, navbar, userProfile: userProfileSettings } = useThemeConfig();
22
22
  const hideSearch =
23
23
  searchSettings?.hide || (searchSettings?.placement && searchSettings?.placement !== 'navbar');
24
+ const hideUserProfile = userProfileSettings?.hide;
24
25
 
25
26
  if (navbar?.hide) {
26
27
  return null;
@@ -43,7 +44,7 @@ export function Navbar({ menu, logo, search, profile }: NavbarProps): JSX.Elemen
43
44
  {logo}
44
45
  <NavbarMenu menuItems={menu} />
45
46
  {hideSearch ? null : search}
46
- {profile}
47
+ {hideUserProfile ? null : profile}
47
48
  <ColorModeSwitcher />
48
49
  </NavbarRow>
49
50
  </NavbarContainer>
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import { useThemeConfig } from '@theme/hooks/useThemeConfig';
5
+
6
+ export interface LoginLinkProps {
7
+ href: string;
8
+ }
9
+
10
+ export function LoginLink({ href }: LoginLinkProps): JSX.Element {
11
+ const { userProfile } = useThemeConfig();
12
+ return <StyledLink href={href}>{userProfile?.loginLabel || 'Login'}</StyledLink>;
13
+ }
14
+
15
+ const StyledLink = styled.a.attrs(() => ({
16
+ 'data-component-name': 'Profile/LoginLink',
17
+ }))`
18
+ display: inline-block;
19
+ color: var(--navbar-text-color);
20
+ text-decoration: none;
21
+ padding: 0 var(--navbar-item-padding-horizontal);
22
+ text-align: center;
23
+ line-height: 1;
24
+ font-size: var(--navbar-item-font-size);
25
+ font-weight: var(--navbar-item-font-weight);
26
+ &:hover {
27
+ color: var(--navbar-item-hover-text-color);
28
+ }
29
+ `;
@@ -44,7 +44,9 @@ function ProfileComponent({ name, imageUrl, onClick, color }: ProfileProps): JSX
44
44
 
45
45
  export const Profile = memo<ProfileProps>(ProfileComponent);
46
46
 
47
- const ProfileWrapper = styled.div`
47
+ const ProfileWrapper = styled.div.attrs(() => ({
48
+ 'data-component-name': 'Profile/Profile',
49
+ }))`
48
50
  display: flex;
49
51
  align-items: center;
50
52
  cursor: pointer;
@@ -0,0 +1,101 @@
1
+ import React, { useState } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import { Link } from '@portal/Link';
5
+ import { Profile } from '@theme/Profile/Profile';
6
+ import { Tooltip } from '@theme/Tooltip/Tooltip';
7
+ import { useThemeConfig } from '@theme/hooks/useThemeConfig';
8
+
9
+ export interface UserProfileProps {
10
+ userInfo: {
11
+ isAuthenticated: boolean;
12
+ name: string;
13
+ picture: string;
14
+ logoutDisabled?: boolean;
15
+ };
16
+ handleLogout: (logoutRedirect?: string) => void;
17
+ hasDeveloperOnboarding?: boolean;
18
+ hasApiLogs?: boolean;
19
+ }
20
+
21
+ export function UserProfile({
22
+ userInfo,
23
+ handleLogout,
24
+ hasDeveloperOnboarding = false,
25
+ hasApiLogs = false,
26
+ }: UserProfileProps): JSX.Element {
27
+ const [isOpened, setIsOpened] = useState<boolean>(false);
28
+
29
+ const { userProfile: userProfileSettings } = useThemeConfig();
30
+
31
+ const logoutRedirect = userProfileSettings?.logoutRedirect || '/';
32
+
33
+ return (
34
+ <StyledTooltip
35
+ isOpen={isOpened}
36
+ withArrow={false}
37
+ className="copy-button"
38
+ placement="bottom"
39
+ width="100%"
40
+ tip={
41
+ <StyledUl>
42
+ {hasDeveloperOnboarding ? (
43
+ <Link to="/apps">
44
+ <StyledLi>My Apps</StyledLi>
45
+ </Link>
46
+ ) : null}
47
+ {hasApiLogs ? (
48
+ <Link to="/api-logs">
49
+ <StyledLi>API logs</StyledLi>
50
+ </Link>
51
+ ) : null}
52
+ <StyledLi onClick={() => handleLogout(logoutRedirect)}>
53
+ {userProfileSettings?.logoutLabel || 'Log out'}
54
+ </StyledLi>
55
+ </StyledUl>
56
+ }
57
+ >
58
+ <Profile
59
+ name={userInfo.name}
60
+ imageUrl={userInfo.picture}
61
+ onClick={userInfo.logoutDisabled ? undefined : () => setIsOpened(!isOpened)}
62
+ />
63
+ </StyledTooltip>
64
+ );
65
+ }
66
+
67
+ const StyledTooltip = styled(Tooltip)`
68
+ > span {
69
+ padding: 0;
70
+ }
71
+ `;
72
+
73
+ const StyledUl = styled.ul`
74
+ margin: 0;
75
+ padding: 0;
76
+ list-style: none;
77
+ text-align: left;
78
+ background-color: var(--search-modal-background);
79
+ color: var(--search-modal-text-color);
80
+ min-width: 100px;
81
+ a {
82
+ text-decoration: none;
83
+ &:hover {
84
+ color: inherit;
85
+ }
86
+ &:visited {
87
+ color: inherit;
88
+ }
89
+ }
90
+ `;
91
+
92
+ const StyledLi = styled.li`
93
+ cursor: pointer;
94
+ font-size: 16px;
95
+ list-style: none;
96
+ padding: 15px 20px;
97
+ transition: background-color 0.25s ease 0s;
98
+ &:hover {
99
+ background-color: rgba(0, 0, 0, 0.1);
100
+ }
101
+ `;
@@ -1,2 +1,6 @@
1
1
  export { Profile } from './Profile';
2
2
  export type { ProfileProps } from './Profile';
3
+ export { LoginLink } from './LoginLink';
4
+ export type { LoginLinkProps } from './LoginLink';
5
+ export { UserProfile } from './UserProfile';
6
+ export type { UserProfileProps } from './UserProfile';
package/src/config.ts CHANGED
@@ -81,6 +81,16 @@ export const ThemeConfig = z
81
81
  .optional(),
82
82
  links: z.array(LinksConfig).optional(),
83
83
 
84
+ feedback: z
85
+ .object({
86
+ type: z.string().default('sentiment'),
87
+ settings: z.string().optional(),
88
+ })
89
+ .extend(HideConfig.shape)
90
+ .strict()
91
+ .default({})
92
+ .optional(),
93
+
84
94
  search: z
85
95
  .object({
86
96
  placement: z.string().default('navbar').optional(),
@@ -159,6 +169,15 @@ export const ThemeConfig = z
159
169
  .optional(),
160
170
  openapi: z.object({}).passthrough().optional(),
161
171
  graphql: z.object({}).passthrough().optional(),
172
+ userProfile: z
173
+ .object({
174
+ loginLabel: z.string().default('Login').optional(),
175
+ logoutLabel: z.string().default('Logout').optional(),
176
+ logoutRedirect: z.string().default('/').optional(),
177
+ })
178
+ .extend(HideConfig.shape)
179
+ .optional()
180
+ .default({}),
162
181
  })
163
182
  .passthrough()
164
183
  .default({});
@@ -1903,6 +1903,10 @@ const tile = css`
1903
1903
  --thin-tile-background-color: var(--color-secondary-50);
1904
1904
  `;
1905
1905
 
1906
+ const apiLogsTable = css`
1907
+ --api-logs-row-hover-background-color: var(--color-secondary-300)
1908
+ `
1909
+
1906
1910
  export const styles = css`
1907
1911
  :root {
1908
1912
  ${baseColors}
@@ -1931,6 +1935,7 @@ export const styles = css`
1931
1935
  ${lastUpdated}
1932
1936
  ${tile}
1933
1937
  ${loadProgressBar}
1938
+ ${apiLogsTable}
1934
1939
  }
1935
1940
 
1936
1941
  :root.dark {
package/src/index.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';
@@ -32,6 +32,12 @@ export function useThemeConfig<T extends Record<string, unknown>>(): T & ThemeUI
32
32
  colorMode: {
33
33
  modes: ['light', 'dark'],
34
34
  },
35
+ feedback: {
36
+ type: 'sentiment',
37
+ },
38
+ userProfile: {
39
+ hide: false,
40
+ },
35
41
  } as ThemeUIConfig as T & ThemeUIConfig;
36
42
  }
37
43
 
package/src/settings.yaml CHANGED
@@ -25,3 +25,5 @@ colorMode:
25
25
  - 'dark'
26
26
  lastUpdatedBlock:
27
27
  hide: false
28
+ userProfile:
29
+ hide: false
@@ -1,2 +1,2 @@
1
1
  export * from './src/shared/types/nav';
2
- export type { MdHeading } from './src/shared/types/markdown';
2
+ export type { MdHeading } from './src/shared/types/markdown';
package/src/ui/Box.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { space, position, flex, textAlign, color, border, layout } from 'styled-system';
1
+ import { space, position, flex, textAlign, color, border, layout, grid } from 'styled-system';
2
2
  import styled from 'styled-components';
3
3
 
4
4
  import type {
@@ -9,6 +9,7 @@ import type {
9
9
  PositionProps,
10
10
  FlexProps,
11
11
  BordersProps,
12
+ GridProps,
12
13
  } from 'styled-system';
13
14
 
14
15
  export interface BoxProps
@@ -18,7 +19,8 @@ export interface BoxProps
18
19
  FlexProps,
19
20
  TextAlignProps,
20
21
  ColorProps,
21
- BordersProps {}
22
+ BordersProps,
23
+ GridProps {}
22
24
 
23
25
  export const Box = styled.div.attrs(() => ({
24
26
  'data-component-name': 'ui/Box',
@@ -31,4 +33,5 @@ export const Box = styled.div.attrs(() => ({
31
33
  ${textAlign}
32
34
  ${color}
33
35
  ${border}
36
+ ${grid}
34
37
  `;