@redocly/theme 0.8.2 → 0.8.3

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 (59) hide show
  1. package/lib/Button/Button.js +2 -2
  2. package/lib/Catalog/Filter.js +1 -3
  3. package/lib/Markdown/Admonition.d.ts +2 -1
  4. package/lib/Markdown/Admonition.js +2 -2
  5. package/lib/Profile/Profile.js +1 -1
  6. package/lib/ReferenceDocs/ClearButton.d.ts +8 -0
  7. package/lib/ReferenceDocs/ClearButton.js +48 -0
  8. package/lib/ReferenceDocs/DevOnboardingTryItSecurity.d.ts +3 -0
  9. package/lib/ReferenceDocs/DevOnboardingTryItSecurity.js +158 -0
  10. package/lib/ReferenceDocs/Dropdown.d.ts +28 -0
  11. package/lib/ReferenceDocs/Dropdown.js +150 -0
  12. package/lib/ReferenceDocs/TryItSecurityApps.d.ts +7 -0
  13. package/lib/ReferenceDocs/TryItSecurityApps.js +15 -0
  14. package/lib/ReferenceDocs/index.d.ts +1 -0
  15. package/lib/ReferenceDocs/index.js +18 -0
  16. package/lib/Sidebar/FooterWrapper.d.ts +3 -0
  17. package/lib/Sidebar/FooterWrapper.js +15 -0
  18. package/lib/Sidebar/HeaderWrapper.d.ts +3 -0
  19. package/lib/Sidebar/HeaderWrapper.js +15 -0
  20. package/lib/Sidebar/MenuContainer.d.ts +5 -3
  21. package/lib/Sidebar/MenuContainer.js +3 -2
  22. package/lib/Sidebar/MenuItemLabel.js +2 -2
  23. package/lib/Sidebar/Sidebar.d.ts +0 -1
  24. package/lib/Sidebar/Sidebar.js +1 -6
  25. package/lib/Sidebar/SidebarLayout.d.ts +4 -2
  26. package/lib/Sidebar/SidebarLayout.js +7 -28
  27. package/lib/Sidebar/index.d.ts +2 -0
  28. package/lib/Sidebar/index.js +2 -0
  29. package/lib/SourceCode/SourceCode.d.ts +3 -2
  30. package/lib/SourceCode/SourceCode.js +5 -5
  31. package/lib/globalStyle.js +8 -0
  32. package/lib/index.d.ts +1 -0
  33. package/lib/index.js +1 -0
  34. package/lib/mocks/hooks/index.d.ts +1 -1
  35. package/lib/mocks/hooks/index.js +3 -1
  36. package/lib/mocks/useGlobalData.d.ts +1 -0
  37. package/lib/mocks/useGlobalData.js +8 -0
  38. package/package.json +1 -1
  39. package/src/Button/Button.tsx +8 -4
  40. package/src/Catalog/Filter.tsx +1 -1
  41. package/src/Markdown/Admonition.tsx +3 -0
  42. package/src/Profile/Profile.tsx +1 -1
  43. package/src/ReferenceDocs/ClearButton.tsx +32 -0
  44. package/src/ReferenceDocs/DevOnboardingTryItSecurity.tsx +161 -0
  45. package/src/ReferenceDocs/Dropdown.tsx +202 -0
  46. package/src/ReferenceDocs/TryItSecurityApps.tsx +17 -0
  47. package/src/ReferenceDocs/index.ts +1 -0
  48. package/src/Sidebar/FooterWrapper.tsx +9 -0
  49. package/src/Sidebar/HeaderWrapper.tsx +9 -0
  50. package/src/Sidebar/MenuContainer.tsx +8 -3
  51. package/src/Sidebar/MenuItemLabel.tsx +2 -2
  52. package/src/Sidebar/Sidebar.tsx +1 -7
  53. package/src/Sidebar/SidebarLayout.tsx +12 -34
  54. package/src/Sidebar/index.ts +2 -0
  55. package/src/SourceCode/SourceCode.tsx +6 -0
  56. package/src/globalStyle.ts +8 -0
  57. package/src/index.ts +1 -0
  58. package/src/mocks/hooks/index.ts +2 -0
  59. package/src/mocks/useGlobalData.tsx +3 -0
@@ -7,14 +7,14 @@ exports.SidebarLayout = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const styled_components_1 = __importDefault(require("styled-components"));
9
9
  const Sidebar_1 = require("../Sidebar/Sidebar");
10
+ const FooterWrapper_1 = require("../Sidebar/FooterWrapper");
11
+ const HeaderWrapper_1 = require("../Sidebar/HeaderWrapper");
10
12
  const useMobileMenu_1 = require("../hooks/useMobileMenu");
11
13
  const MobileSidebarButton_1 = require("../Sidebar/MobileSidebarButton");
12
14
  const MenuContainer_1 = require("../Sidebar/MenuContainer");
13
15
  const SidebarSearch_1 = require("../Search/SidebarSearch");
14
16
  const useThemeConfig_1 = require("../hooks/useThemeConfig");
15
- const ArrowBack_1 = require("../Sidebar/ArrowBack");
16
- const Link_1 = require("../mocks/Link");
17
- function SidebarLayout({ versions, menu, backLink, isNavbar, }) {
17
+ function SidebarLayout({ versions, menu, footer, header, growContent, }) {
18
18
  const [isOpen, setIsOpen] = (0, useMobileMenu_1.useMobileMenu)();
19
19
  const toggleMenu = () => setIsOpen(!isOpen);
20
20
  const { search, sidebar } = (0, useThemeConfig_1.useThemeConfig)();
@@ -24,33 +24,12 @@ function SidebarLayout({ versions, menu, backLink, isNavbar, }) {
24
24
  return (react_1.default.createElement(Wrapper, { "data-component-name": "Sidebar/SidebarLayout" },
25
25
  react_1.default.createElement(MobileSidebarButton_1.MobileSidebarButton, { opened: isOpen, onClick: toggleMenu }),
26
26
  !(search === null || search === void 0 ? void 0 : search.hide) && (search === null || search === void 0 ? void 0 : search.placement) === 'sidebar' ? react_1.default.createElement(SidebarSearch_1.SidebarSearch, null) : null,
27
- react_1.default.createElement(Sidebar_1.Sidebar, { animate: true, opened: isOpen, isNavbar: isNavbar },
28
- (backLink && (react_1.default.createElement(BackLinkWrapper, null,
29
- react_1.default.createElement(Link_1.Link, { to: backLink.slug },
30
- react_1.default.createElement(ArrowBack_1.ArrowBack, null),
31
- "Back to ",
32
- backLink.label)))) ||
33
- null,
27
+ react_1.default.createElement(Sidebar_1.Sidebar, { animate: true, opened: isOpen },
28
+ header ? react_1.default.createElement(HeaderWrapper_1.HeaderWrapper, null, header) : null,
34
29
  versions,
35
- react_1.default.createElement(MenuContainer_1.MenuContainer, null, menu))));
30
+ react_1.default.createElement(MenuContainer_1.MenuContainer, { growContent: growContent }, menu),
31
+ footer ? react_1.default.createElement(FooterWrapper_1.FooterWrapper, null, footer) : null)));
36
32
  }
37
33
  exports.SidebarLayout = SidebarLayout;
38
- const BackLinkWrapper = styled_components_1.default.div `
39
- padding: var(--sidebar-offset-top) var(--sidebar-item-padding-horizontal)
40
- var(--sidebar-item-padding-horizontal)
41
- calc(var(--sidebar-offset-left) + var(--sidebar-item-padding-horizontal));
42
-
43
- a {
44
- color: var(--sidebar-back-button-text-color);
45
- font-size: var(--sidebar-back-button-font-size);
46
- font-family: var(--sidebar-back-button-font-family);
47
- text-decoration: none;
48
- &:hover {
49
- color: var(--sidebar-back-button-hover-text-color);
50
- }
51
- }
52
-
53
- border-bottom: 1px solid var(--sidebar-border-color);
54
- `;
55
34
  const Wrapper = styled_components_1.default.div ``;
56
35
  //# sourceMappingURL=SidebarLayout.js.map
@@ -17,3 +17,5 @@ export * from '../Sidebar/Separator';
17
17
  export * from '../Sidebar/SeparatorItem';
18
18
  export * from '../Sidebar/Sidebar';
19
19
  export * from '../Sidebar/SidebarLayout';
20
+ export * from '../Sidebar/FooterWrapper';
21
+ export * from '../Sidebar/HeaderWrapper';
@@ -33,4 +33,6 @@ __exportStar(require("../Sidebar/Separator"), exports);
33
33
  __exportStar(require("../Sidebar/SeparatorItem"), exports);
34
34
  __exportStar(require("../Sidebar/Sidebar"), exports);
35
35
  __exportStar(require("../Sidebar/SidebarLayout"), exports);
36
+ __exportStar(require("../Sidebar/FooterWrapper"), exports);
37
+ __exportStar(require("../Sidebar/HeaderWrapper"), exports);
36
38
  //# sourceMappingURL=index.js.map
@@ -2,6 +2,7 @@
2
2
  interface CommonCodeProps {
3
3
  withLineNumbers?: boolean;
4
4
  startLineNumber?: number;
5
+ className?: string;
5
6
  }
6
7
  export interface SourceCodeProps extends CommonCodeProps {
7
8
  lang: string;
@@ -27,6 +28,6 @@ export interface ExternalSource {
27
28
  properties?: any;
28
29
  operation?: any;
29
30
  }
30
- export declare function Code({ source, lang, dataTestId, withLineNumbers, startLineNumber, }: CodeProps): JSX.Element;
31
- export declare function SourceCode({ lang, source, externalSource, onCopyClick, withCopyButton, dataTestId, withLineNumbers, startLineNumber, }: SourceCodeProps): JSX.Element;
31
+ export declare function Code({ source, lang, dataTestId, withLineNumbers, startLineNumber, className, }: CodeProps): JSX.Element;
32
+ export declare function SourceCode({ lang, source, externalSource, onCopyClick, withCopyButton, dataTestId, withLineNumbers, startLineNumber, className, }: SourceCodeProps): JSX.Element;
32
33
  export {};
@@ -28,16 +28,16 @@ const react_1 = __importStar(require("react"));
28
28
  const utils_1 = require("../utils");
29
29
  const SamplesPanelControls_1 = require("../SamplesPanelControls");
30
30
  const CopyButton_1 = require("../CopyButton");
31
- function Code({ source, lang, dataTestId, withLineNumbers, startLineNumber, }) {
31
+ function Code({ source, lang, dataTestId, withLineNumbers, startLineNumber, className, }) {
32
32
  const highlightedCode = (0, utils_1.highlight)(source, lang);
33
- return (react_1.default.createElement(SamplesPanelControls_1.PreformattedCodeBlock, { dangerouslySetInnerHTML: {
33
+ return (react_1.default.createElement(SamplesPanelControls_1.PreformattedCodeBlock, { className: className, dangerouslySetInnerHTML: {
34
34
  __html: withLineNumbers
35
35
  ? (0, utils_1.addLineNumbers)(highlightedCode, startLineNumber)
36
36
  : highlightedCode,
37
37
  }, "data-cy": dataTestId }));
38
38
  }
39
39
  exports.Code = Code;
40
- function SourceCode({ lang, source, externalSource, onCopyClick, withCopyButton, dataTestId = 'source-code', withLineNumbers, startLineNumber, }) {
40
+ function SourceCode({ lang, source, externalSource, onCopyClick, withCopyButton, dataTestId = 'source-code', withLineNumbers, startLineNumber, className, }) {
41
41
  const [sourceCode, setSourceCode] = (0, react_1.useState)(source !== null && source !== void 0 ? source : '');
42
42
  // The same initial value should be returned for ssr and frontend to avoid issues
43
43
  // Because we don't have session storage in ssr and can't get the security details there
@@ -52,9 +52,9 @@ function SourceCode({ lang, source, externalSource, onCopyClick, withCopyButton,
52
52
  if (withCopyButton) {
53
53
  return (react_1.default.createElement(CopyButton_1.CopyButtonWrapper, { data: sourceCode, onCopyClick: onCopyClick, "data-component-name": "SourceCode/SourceCode" }, ({ renderCopyButton }) => (react_1.default.createElement(SamplesPanelControls_1.SampleControlsWrap, null,
54
54
  react_1.default.createElement(SamplesPanelControls_1.SampleControls, { "data-cy": "copy-button" }, renderCopyButton()),
55
- react_1.default.createElement(Code, { lang: lang, source: sourceCode, withLineNumbers: withLineNumbers, startLineNumber: startLineNumber, dataTestId: dataTestId })))));
55
+ react_1.default.createElement(Code, { lang: lang, source: sourceCode, withLineNumbers: withLineNumbers, startLineNumber: startLineNumber, dataTestId: dataTestId, className: className })))));
56
56
  }
57
- return (react_1.default.createElement(Code, { dataTestId: dataTestId, lang: lang, source: sourceCode, withLineNumbers: withLineNumbers, startLineNumber: startLineNumber, "data-component-name": "SourceCode/SourceCode" }));
57
+ return (react_1.default.createElement(Code, { dataTestId: dataTestId, className: className, lang: lang, source: sourceCode, withLineNumbers: withLineNumbers, startLineNumber: startLineNumber, "data-component-name": "SourceCode/SourceCode" }));
58
58
  }
59
59
  exports.SourceCode = SourceCode;
60
60
  //# sourceMappingURL=SourceCode.js.map
@@ -1982,10 +1982,18 @@ exports.styles = (0, styled_components_1.css) `
1982
1982
  ${apiLogsTable}
1983
1983
  ${pages}
1984
1984
  ${modal}
1985
+
1986
+ --api-onboarding-table-text-color: #4e5356;
1987
+
1988
+ background-color: var(--background-color);
1989
+ color: var(--text-color);
1990
+ font-family: var(--font-family-base);
1985
1991
  }
1986
1992
 
1987
1993
  :root.dark {
1988
1994
  ${darkColors_1.darkMode};
1995
+
1996
+ --api-onboarding-table-text-color: #ffffff;
1989
1997
  }
1990
1998
 
1991
1999
  :root.notransition * {
package/lib/index.d.ts CHANGED
@@ -23,3 +23,4 @@ export * from './types/config';
23
23
  export * from './config';
24
24
  export * from './Pages';
25
25
  export * from './Markdown';
26
+ export * from './ReferenceDocs';
package/lib/index.js CHANGED
@@ -39,4 +39,5 @@ __exportStar(require("./types/config"), exports);
39
39
  __exportStar(require("./config"), exports);
40
40
  __exportStar(require("./Pages"), exports);
41
41
  __exportStar(require("./Markdown"), exports);
42
+ __exportStar(require("./ReferenceDocs"), exports);
42
43
  //# sourceMappingURL=index.js.map
@@ -13,4 +13,4 @@ export declare function useSidebarSiblingsData(): {
13
13
  };
14
14
  export declare function usePageSharedData<T = unknown>(_id: string): T;
15
15
  export declare function useCatalog(_items: ResolvedNavItem[], _config: CatalogConfig): FilteredCatalog;
16
- export {};
16
+ export { useGlobalData } from '../useGlobalData';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useCatalog = exports.usePageSharedData = exports.useSidebarSiblingsData = exports.useThemeConfig = void 0;
3
+ exports.useGlobalData = exports.useCatalog = exports.usePageSharedData = exports.useSidebarSiblingsData = exports.useThemeConfig = void 0;
4
4
  function useThemeConfig() {
5
5
  return {
6
6
  search: {
@@ -69,4 +69,6 @@ function useCatalog(_items, _config) {
69
69
  throw new Error('Mock not implemented yet.');
70
70
  }
71
71
  exports.useCatalog = useCatalog;
72
+ var useGlobalData_1 = require("../useGlobalData");
73
+ Object.defineProperty(exports, "useGlobalData", { enumerable: true, get: function () { return useGlobalData_1.useGlobalData; } });
72
74
  //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ export declare function useGlobalData(): Record<string, unknown>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useGlobalData = void 0;
4
+ function useGlobalData() {
5
+ return {};
6
+ }
7
+ exports.useGlobalData = useGlobalData;
8
+ //# sourceMappingURL=useGlobalData.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Shared UI components lib",
5
5
  "author": "team@redocly.com",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -67,10 +67,14 @@ export const baseButtonStyles = css`
67
67
  }
68
68
  `;
69
69
 
70
- const StyledButton = styled.button.attrs(({ color = 'default', extraClass }: ButtonProps) => ({
71
- className: `button-color-${color}${extraClass ? ` ${extraClass}` : ''}`,
72
- 'data-component-name': 'Button/Button',
73
- }))<ButtonProps>`
70
+ const StyledButton = styled.button.attrs(
71
+ ({ color = 'default', extraClass, variant }: ButtonProps) => ({
72
+ className: `button-color-${color}${variant ? ` button-variant-${variant}` : ''}${
73
+ extraClass ? ` ${extraClass}` : ''
74
+ }`,
75
+ 'data-component-name': 'Button/Button',
76
+ }),
77
+ )<ButtonProps>`
74
78
  text-decoration: none;
75
79
  text-align: center;
76
80
 
@@ -8,7 +8,7 @@ export function Filter({ filter }: { filter: ResolvedFilter }) {
8
8
  if (!filter.parentUsed) return null;
9
9
  return (
10
10
  <FilterGroup key={filter.property + filter.title}>
11
- <FilterTitle>{filter.title} filter</FilterTitle>
11
+ <FilterTitle>{filter.title}</FilterTitle>
12
12
  {filter.type === 'select' ? (
13
13
  <StyledSelect
14
14
  onChange={(e) => filter.selectOption(e.target.value)}
@@ -9,6 +9,7 @@ interface AdmonitionTypeProps {
9
9
 
10
10
  export interface AdmonitionProps extends Partial<AdmonitionTypeProps> {
11
11
  name?: string;
12
+ className?: string;
12
13
  'data-source'?: string;
13
14
  'data-hash'?: string;
14
15
  }
@@ -17,12 +18,14 @@ export function Admonition({
17
18
  type = 'info',
18
19
  name,
19
20
  children,
21
+ className,
20
22
  'data-source': dataSource,
21
23
  'data-hash': dataHash,
22
24
  }: React.PropsWithChildren<AdmonitionProps>): JSX.Element {
23
25
  return (
24
26
  <Wrapper
25
27
  type={type}
28
+ className={className}
26
29
  data-component-name="Markdown/Admonition"
27
30
  data-source={dataSource}
28
31
  data-hash={dataHash}
@@ -10,7 +10,7 @@ export interface ProfileProps {
10
10
  onClick?: () => void;
11
11
  }
12
12
 
13
- function ProfileComponent({ name, imageUrl, onClick, color }: ProfileProps): JSX.Element {
13
+ function ProfileComponent({ name = 'User', imageUrl, onClick, color }: ProfileProps): JSX.Element {
14
14
  if (imageUrl) {
15
15
  return (
16
16
  <ProfileWrapper onClick={onClick}>
@@ -0,0 +1,32 @@
1
+ import React, { memo } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ // FIXME!!!
5
+
6
+ interface ClearButtonProps {
7
+ className?: string;
8
+ style?: React.CSSProperties;
9
+ handleClear?: () => void;
10
+ }
11
+
12
+ const ClearButtonComponent = ({ className, style, handleClear }: ClearButtonProps): JSX.Element => (
13
+ <button className={className} style={style} onClick={handleClear}>
14
+
15
+ </button>
16
+ );
17
+
18
+ export const ClearButton = styled(memo<ClearButtonProps>(ClearButtonComponent))`
19
+ z-index: 1;
20
+ position: absolute;
21
+ right: 24px;
22
+ top: 50%;
23
+ transform: translateY(-50%);
24
+ color: #89949f;
25
+ background: none;
26
+ border: none;
27
+ cursor: pointer;
28
+
29
+ &:hover {
30
+ color: #fff;
31
+ }
32
+ `;
@@ -0,0 +1,161 @@
1
+ import * as React from 'react';
2
+ import { Button } from '@theme';
3
+ import styled from 'styled-components';
4
+
5
+ import type { TryItSecurityAppsProps } from './TryItSecurityApps';
6
+
7
+ import { usePageData } from '@portal/hooks/usePageData';
8
+
9
+ import { Dropdown } from './Dropdown';
10
+
11
+ export function DevOnboardingTryItSecurity(props: TryItSecurityAppsProps) {
12
+ const apiId = (props as any).apiId;
13
+
14
+ // @ts-ignore
15
+ const { userData } = usePageData('userData') || {};
16
+
17
+ // TODO: handle error
18
+ const [_error, setError] = React.useState<string | undefined>();
19
+ const [apps, setApps] = React.useState<{ id: string; title: string }[] | undefined>();
20
+ const [appId, setAppId] = React.useState<string | undefined>('');
21
+ const [loading, setLoading] = React.useState(true);
22
+ const [appLoading, setAppLoading] = React.useState(false);
23
+
24
+ const [selectedAppSecret, setSelectedAppSecret] = React.useState<string | undefined>();
25
+
26
+ const [acceptValueUpdate, setAcceptValueUpdate] = React.useState(true);
27
+ const initialRender = React.useRef(true);
28
+
29
+ React.useEffect(() => {
30
+ if (!props.value && initialRender.current) return;
31
+ if (acceptValueUpdate) {
32
+ setAcceptValueUpdate(false);
33
+ } else {
34
+ setAppId('');
35
+ }
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ }, [props.value]);
38
+
39
+ React.useEffect(() => {
40
+ initialRender.current = false;
41
+ const prevAppId = sessionStorage.getItem('redocly_onboarding:prevAppId');
42
+ if (prevAppId) {
43
+ setAppId(prevAppId);
44
+ }
45
+ if (userData?.isAuthenticated) {
46
+ fetch(`/api/api-keys/api-products/${apiId}/access?expand=1`).then((res) => {
47
+ return res
48
+ .json()
49
+ .then((data: any) => {
50
+ if (res.ok) {
51
+ setApps(data.apps);
52
+ } else {
53
+ setError(data.message);
54
+ }
55
+ })
56
+ .catch(() => setError('Something went wrong'))
57
+ .finally(() => setLoading(false));
58
+ });
59
+ }
60
+ }, [apiId, userData?.isAuthenticated]);
61
+
62
+ React.useEffect(() => {
63
+ sessionStorage.setItem('redocly_onboarding:prevAppId', appId || '');
64
+ }, [appId]);
65
+
66
+ if (!apiId) {
67
+ return null;
68
+ }
69
+
70
+ const loadAppCredentials = (appId: string) => {
71
+ setAppLoading(true);
72
+ fetch(`/api/api-keys/apps/${appId}`).then((res) => {
73
+ return res
74
+ .json()
75
+ .then((data: any) => {
76
+ if (res.ok) {
77
+ const cred = data.credentials.find((cred: any) => cred.status === 'approved');
78
+ setAcceptValueUpdate(true);
79
+ setSelectedAppSecret(cred.clientSecret);
80
+ } else {
81
+ setError(data.message);
82
+ }
83
+ })
84
+ .catch(() => setError('Something went wrong'))
85
+ .finally(() => setAppLoading(false));
86
+ });
87
+ };
88
+
89
+ const placeholder = loading ? 'Loading your apps...' : undefined;
90
+
91
+ return (
92
+ <>
93
+ <FormLabel htmlFor="server">Select app: </FormLabel>
94
+ <FormControl>
95
+ <TryItDropdown
96
+ placeholder={placeholder}
97
+ value={appId || placeholder}
98
+ options={apps?.map((app) => ({ title: app.title, value: app.id })) || []}
99
+ fullWidth
100
+ onChange={({ value }: { value: string }) => {
101
+ setAppId(value);
102
+ loadAppCredentials(value);
103
+ }}
104
+ />
105
+ <UseKeyButton
106
+ disabled={!selectedAppSecret || appLoading}
107
+ color="secondary"
108
+ blinking={appLoading}
109
+ onClick={() => selectedAppSecret && props.onChange(selectedAppSecret)}
110
+ >
111
+ Use key
112
+ </UseKeyButton>
113
+ </FormControl>
114
+ </>
115
+ );
116
+ }
117
+
118
+ const TryItDropdown = styled(Dropdown)`
119
+ .dropdown-select,
120
+ label {
121
+ background-color: var(--panel-try-it-dropdown-background-color);
122
+ border: var(--panel-try-it-dropdown-border);
123
+ color: var(--panel-try-it-dropdown-color);
124
+ }
125
+ `;
126
+
127
+ const FormControl = styled.div`
128
+ width: 100%;
129
+ display: flex;
130
+ flex-direction: column;
131
+ margin-bottom: 10px;
132
+
133
+ &:last-child {
134
+ margin-bottom: 0;
135
+ }
136
+ `;
137
+
138
+ const FormLabel = styled.label<{ required?: boolean }>`
139
+ padding-bottom: 6px;
140
+ display: block;
141
+ white-space: nowrap;
142
+ line-height: 1em;
143
+
144
+ ${({ required }) =>
145
+ required &&
146
+ `
147
+ &:after {
148
+ display: inline-block;
149
+ content: '*';
150
+ margin-left: 4px;
151
+ color: var(--color-error-500);
152
+ }
153
+ `}
154
+ `;
155
+
156
+ const UseKeyButton = styled(Button)`
157
+ width: 100px;
158
+ align-self: flex-end;
159
+ margin-top: 16px;
160
+ margin-right: 0;
161
+ `;
@@ -0,0 +1,202 @@
1
+ import React, { memo, useMemo } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ export interface DropdownOption {
5
+ idx?: number;
6
+ value: string;
7
+ title?: string;
8
+ serverUrl?: string;
9
+ label?: string;
10
+ }
11
+
12
+ export interface DropdownProps {
13
+ options: DropdownOption[];
14
+ onChange: (option: DropdownOption) => void;
15
+ handleClear?: () => void;
16
+ clearable?: boolean;
17
+ ariaLabel?: string;
18
+ className?: string;
19
+ placeholder?: string;
20
+ value?: string;
21
+ dense?: boolean;
22
+ fullWidth?: boolean;
23
+ variant?: 'dark' | 'light';
24
+ }
25
+
26
+ export interface ArrowIconProps {
27
+ className?: string;
28
+ variant?: 'light' | 'dark';
29
+ style?: React.CSSProperties;
30
+ }
31
+
32
+ import { ClearButton } from './ClearButton';
33
+
34
+ const ArrowSvg = ({ className, style }: ArrowIconProps): JSX.Element => (
35
+ <svg
36
+ className={className}
37
+ style={style}
38
+ xmlns="http://www.w3.org/2000/svg"
39
+ width="16"
40
+ height="16"
41
+ viewBox="0 0 24 24"
42
+ fill="none"
43
+ stroke="currentColor"
44
+ strokeWidth="2"
45
+ strokeLinecap="round"
46
+ strokeLinejoin="round"
47
+ >
48
+ <polyline points="6 9 12 15 18 9" />
49
+ </svg>
50
+ );
51
+
52
+ const ArrowIcon = styled(ArrowSvg)`
53
+ position: absolute;
54
+ pointer-events: none;
55
+ z-index: 1;
56
+ top: 50%;
57
+ -webkit-transform: translateY(-50%);
58
+ -ms-transform: translateY(-50%);
59
+ transform: translateY(-50%);
60
+ right: 8px;
61
+ margin: auto;
62
+ text-align: center;
63
+ polyline {
64
+ color: ${(props) => props.variant === 'dark' && 'white'};
65
+ }
66
+ `;
67
+
68
+ const DropdownComponent = ({
69
+ options,
70
+ onChange,
71
+ handleClear,
72
+ clearable,
73
+ placeholder,
74
+ value = '',
75
+ className,
76
+ variant = 'light',
77
+ }: DropdownProps): JSX.Element => {
78
+ const handleOnChange = (event: any) => {
79
+ const { selectedIndex } = event.target;
80
+ const index = placeholder || !value ? selectedIndex - 1 : selectedIndex;
81
+ onChange(options[index]);
82
+ };
83
+
84
+ const renderOptions = useMemo(
85
+ () =>
86
+ options.map(({ idx, value, title }: DropdownOption, index) => {
87
+ const normalizedValue = normalizeText(value);
88
+ return (
89
+ <option
90
+ key={idx || normalizedValue + index}
91
+ value={normalizedValue}
92
+ className="dropdown-option"
93
+ >
94
+ {title || normalizedValue}
95
+ </option>
96
+ );
97
+ }),
98
+ [options],
99
+ );
100
+
101
+ const normalizedValue = normalizeText(value);
102
+ const title = options.find((option) => option.value === value)?.title || normalizedValue;
103
+
104
+ return (
105
+ <div className={className + ' dropdown-wrapper'}>
106
+ <ArrowIcon variant={variant} />
107
+ {clearable && normalizedValue?.length > 0 && <ClearButton handleClear={handleClear} />}
108
+ <select onChange={handleOnChange} value={normalizedValue} className="dropdown-select">
109
+ {placeholder && (
110
+ <option disabled hidden value={placeholder}>
111
+ {placeholder}
112
+ </option>
113
+ )}
114
+ {!normalizedValue && !placeholder && <option disabled />}
115
+ {renderOptions}
116
+ </select>
117
+ <label>{title}</label>
118
+ </div>
119
+ );
120
+ };
121
+
122
+ export const Dropdown = styled(memo<DropdownProps>(DropdownComponent))`
123
+
124
+ box-sizing: border-box;
125
+ outline: none;
126
+ display: inline-block;
127
+ border-radius: var(--border-radius);
128
+ vertical-align: bottom;
129
+ position: relative;
130
+ width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
131
+ cursor: pointer;
132
+ text-transform: none;
133
+
134
+ label {
135
+ box-sizing: border-box;
136
+ min-width: 90px;
137
+ height: 36px;
138
+ outline: none;
139
+ display: inline-block;
140
+ color: var(--dropdown-text-color);
141
+ border-radius: var(--border-radius);
142
+ border: var(--dropdown-border);
143
+ padding: var(--dropdown-padding);
144
+ vertical-align: bottom;
145
+ width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
146
+ text-transform: none;
147
+ line-height: inherit;
148
+ font-size: var(--dropdown-font-size);
149
+ font-family: inherit;
150
+ text-overflow: ellipsis;
151
+ overflow: hidden;
152
+ white-space: nowrap;
153
+
154
+ &,
155
+ &:hover,
156
+ &:focus-within {
157
+ border: 1px solid var(--border-color);
158
+ box-shadow: none;
159
+ }
160
+
161
+ ${({ variant }) => (variant === 'dark' ? darkDropdownStyle : '')};
162
+ }
163
+
164
+ .dropdown-select {
165
+ position: absolute;
166
+ top: 0;
167
+ left: 0;
168
+ width: 100%;
169
+ height: 100%;
170
+ opacity: 0;
171
+ border: none;
172
+ appearance: none;
173
+ cursor: pointer;
174
+
175
+ color: var(--text-color);
176
+ line-height: inherit;
177
+ font-size: 14px;
178
+ font-family: inherit;
179
+ padding: var(--dropdown-padding);
180
+ ${({ variant }) => (variant === 'dark' ? darkDropdownStyle : '')};
181
+
182
+ `;
183
+
184
+ const darkDropdownStyle = `
185
+ background-color: var(--panel-samples-dropdown-background-color);
186
+ border: var(--panel-samples-dropdown-border);
187
+ color: var(--panel-samples-dropdown-color);
188
+
189
+ &,
190
+ &:hover,
191
+ &:focus-within {
192
+ border: none;
193
+ box-shadow: none;
194
+ }
195
+ `;
196
+
197
+ function isString<T>(value: T | string): value is string {
198
+ return typeof value === 'string';
199
+ }
200
+
201
+ export const normalizeText = (description?: string | Record<string, any>): string =>
202
+ isString(description) ? description : description?.raw;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+
3
+ import { useGlobalData } from '@portal/hooks';
4
+
5
+ import { DevOnboardingTryItSecurity } from './DevOnboardingTryItSecurity';
6
+
7
+ export type TryItSecurityAppsProps = {
8
+ apiId?: string;
9
+ value?: string;
10
+ onChange: (newSecretKey: string) => void;
11
+ };
12
+
13
+ export function TryItSecurityApps(props: TryItSecurityAppsProps) {
14
+ const { hasDeveloperOnboarding } = useGlobalData() || {};
15
+
16
+ return hasDeveloperOnboarding && props.apiId ? <DevOnboardingTryItSecurity {...props} /> : null;
17
+ }