docusaurus-theme-openapi-docs 4.7.1 → 5.0.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 (127) hide show
  1. package/lib/index.js +2 -0
  2. package/lib/markdown/schema.js +63 -9
  3. package/lib/theme/ApiExplorer/Accept/index.js +2 -1
  4. package/lib/theme/ApiExplorer/Authorization/index.js +12 -18
  5. package/lib/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.js +0 -4
  6. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.d.ts +5 -1
  7. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.js +190 -37
  8. package/lib/theme/ApiExplorer/Body/index.js +84 -13
  9. package/lib/theme/ApiExplorer/Body/slice.d.ts +136 -544
  10. package/lib/theme/ApiExplorer/CodeSnippets/index.d.ts +2 -1
  11. package/lib/theme/ApiExplorer/CodeSnippets/index.js +4 -0
  12. package/lib/theme/ApiExplorer/CodeTabs/index.js +15 -16
  13. package/lib/theme/ApiExplorer/ContentType/index.js +7 -2
  14. package/lib/theme/ApiExplorer/EncodingSelection/slice.d.ts +17 -0
  15. package/lib/theme/ApiExplorer/EncodingSelection/slice.js +29 -0
  16. package/lib/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.d.ts +12 -0
  17. package/lib/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.js +39 -0
  18. package/lib/theme/ApiExplorer/FormItem/_FormItem.scss +0 -5
  19. package/lib/theme/ApiExplorer/FormItem/index.d.ts +1 -4
  20. package/lib/theme/ApiExplorer/FormItem/index.js +2 -26
  21. package/lib/theme/ApiExplorer/FormLabel/_FormLabel.scss +4 -0
  22. package/lib/theme/ApiExplorer/FormLabel/index.d.ts +9 -0
  23. package/lib/theme/ApiExplorer/FormLabel/index.js +50 -0
  24. package/lib/theme/ApiExplorer/FormMultiSelect/index.d.ts +4 -1
  25. package/lib/theme/ApiExplorer/FormMultiSelect/index.js +97 -19
  26. package/lib/theme/ApiExplorer/FormSelect/index.d.ts +6 -1
  27. package/lib/theme/ApiExplorer/FormSelect/index.js +96 -15
  28. package/lib/theme/ApiExplorer/FormTextInput/index.d.ts +4 -1
  29. package/lib/theme/ApiExplorer/FormTextInput/index.js +71 -1
  30. package/lib/theme/ApiExplorer/MethodEndpoint/index.js +28 -0
  31. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.d.ts +4 -1
  32. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.js +11 -3
  33. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.d.ts +4 -1
  34. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.js +4 -1
  35. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.d.ts +4 -1
  36. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.js +6 -2
  37. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.d.ts +4 -1
  38. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.js +6 -2
  39. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.d.ts +4 -1
  40. package/lib/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.js +8 -3
  41. package/lib/theme/ApiExplorer/ParamOptions/_ParamOptions.scss +0 -9
  42. package/lib/theme/ApiExplorer/ParamOptions/index.d.ts +10 -0
  43. package/lib/theme/ApiExplorer/ParamOptions/index.js +55 -5
  44. package/lib/theme/ApiExplorer/Request/_Request.scss +11 -0
  45. package/lib/theme/ApiExplorer/Request/index.js +19 -5
  46. package/lib/theme/ApiExplorer/Request/makeRequest.d.ts +3 -1
  47. package/lib/theme/ApiExplorer/Request/makeRequest.js +19 -3
  48. package/lib/theme/ApiExplorer/Response/_Response.scss +11 -0
  49. package/lib/theme/ApiExplorer/Response/index.js +98 -12
  50. package/lib/theme/ApiExplorer/Server/index.d.ts +4 -1
  51. package/lib/theme/ApiExplorer/Server/index.js +6 -3
  52. package/lib/theme/ApiExplorer/buildPostmanRequest.d.ts +4 -1
  53. package/lib/theme/ApiExplorer/buildPostmanRequest.js +46 -5
  54. package/lib/theme/ApiExplorer/index.js +1 -0
  55. package/lib/theme/ApiExplorer/persistenceMiddleware.d.ts +2 -0
  56. package/lib/theme/ApiItem/hooks.d.ts +1 -0
  57. package/lib/theme/ApiItem/index.js +2 -1
  58. package/lib/theme/ApiItem/store.d.ts +6 -0
  59. package/lib/theme/ApiItem/store.js +11 -7
  60. package/lib/theme/ApiTabs/index.js +10 -11
  61. package/lib/theme/DiscriminatorTabs/index.js +10 -11
  62. package/lib/theme/MimeTabs/index.js +10 -11
  63. package/lib/theme/OperationTabs/index.js +10 -11
  64. package/lib/theme/ParamsItem/index.js +27 -0
  65. package/lib/theme/RequestSchema/index.js +172 -109
  66. package/lib/theme/ResponseHeaders/index.js +0 -1
  67. package/lib/theme/Schema/index.d.ts +1 -1
  68. package/lib/theme/Schema/index.js +91 -23
  69. package/lib/theme/SchemaItem/index.js +6 -1
  70. package/lib/theme/SchemaTabs/index.d.ts +1 -1
  71. package/lib/theme/SchemaTabs/index.js +31 -12
  72. package/lib/theme/styles.scss +1 -0
  73. package/lib/theme/translationIds.d.ts +3 -0
  74. package/lib/theme/translationIds.js +3 -0
  75. package/package.json +9 -8
  76. package/src/index.ts +2 -0
  77. package/src/markdown/schema.ts +69 -13
  78. package/src/theme/ApiExplorer/Accept/index.tsx +2 -1
  79. package/src/theme/ApiExplorer/Authorization/index.tsx +27 -33
  80. package/src/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.tsx +0 -5
  81. package/src/theme/ApiExplorer/Body/FormBodyItem/index.tsx +115 -37
  82. package/src/theme/ApiExplorer/Body/index.tsx +85 -17
  83. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +9 -1
  84. package/src/theme/ApiExplorer/CodeTabs/index.tsx +19 -19
  85. package/src/theme/ApiExplorer/ContentType/index.tsx +7 -4
  86. package/src/theme/ApiExplorer/EncodingSelection/slice.ts +31 -0
  87. package/src/theme/ApiExplorer/EncodingSelection/useResolvedEncoding.ts +43 -0
  88. package/src/theme/ApiExplorer/FormItem/_FormItem.scss +0 -5
  89. package/src/theme/ApiExplorer/FormItem/index.tsx +2 -16
  90. package/src/theme/ApiExplorer/FormLabel/_FormLabel.scss +4 -0
  91. package/src/theme/ApiExplorer/FormLabel/index.tsx +43 -0
  92. package/src/theme/ApiExplorer/FormMultiSelect/index.tsx +40 -20
  93. package/src/theme/ApiExplorer/FormSelect/index.tsx +41 -15
  94. package/src/theme/ApiExplorer/FormTextInput/index.tsx +15 -1
  95. package/src/theme/ApiExplorer/MethodEndpoint/index.tsx +21 -0
  96. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamArrayFormItem.tsx +13 -2
  97. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamBooleanFormItem.tsx +12 -1
  98. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamMultiSelectFormItem.tsx +14 -2
  99. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamSelectFormItem.tsx +14 -2
  100. package/src/theme/ApiExplorer/ParamOptions/ParamFormItems/ParamTextFormItem.tsx +16 -3
  101. package/src/theme/ApiExplorer/ParamOptions/_ParamOptions.scss +0 -9
  102. package/src/theme/ApiExplorer/ParamOptions/index.tsx +97 -11
  103. package/src/theme/ApiExplorer/Request/_Request.scss +11 -0
  104. package/src/theme/ApiExplorer/Request/index.tsx +20 -8
  105. package/src/theme/ApiExplorer/Request/makeRequest.ts +19 -3
  106. package/src/theme/ApiExplorer/Response/_Response.scss +11 -0
  107. package/src/theme/ApiExplorer/Response/index.tsx +35 -14
  108. package/src/theme/ApiExplorer/Server/index.tsx +10 -3
  109. package/src/theme/ApiExplorer/buildPostmanRequest.ts +52 -5
  110. package/src/theme/ApiExplorer/index.tsx +1 -0
  111. package/src/theme/ApiItem/index.tsx +2 -1
  112. package/src/theme/ApiItem/store.ts +2 -0
  113. package/src/theme/ApiTabs/index.tsx +14 -19
  114. package/src/theme/DiscriminatorTabs/index.tsx +14 -19
  115. package/src/theme/MimeTabs/index.tsx +15 -19
  116. package/src/theme/OperationTabs/index.tsx +14 -19
  117. package/src/theme/ParamsItem/index.tsx +25 -0
  118. package/src/theme/RequestSchema/index.tsx +141 -83
  119. package/src/theme/ResponseHeaders/index.tsx +1 -2
  120. package/src/theme/Schema/index.tsx +112 -27
  121. package/src/theme/SchemaItem/index.tsx +6 -1
  122. package/src/theme/SchemaTabs/index.tsx +42 -21
  123. package/src/theme/styles.scss +1 -0
  124. package/src/theme/translationIds.ts +3 -0
  125. package/src/theme-classic.d.ts +25 -1
  126. package/src/types.d.ts +7 -0
  127. package/tsconfig.tsbuildinfo +1 -1
@@ -93,6 +93,8 @@ function Body({
93
93
  const rawSchema = requestBodyMetadata?.content?.[contentType]?.schema;
94
94
  const example = requestBodyMetadata?.content?.[contentType]?.example;
95
95
  const examples = requestBodyMetadata?.content?.[contentType]?.examples;
96
+ const encoding: Record<string, { contentType?: string }> | undefined =
97
+ requestBodyMetadata?.content?.[contentType]?.encoding;
96
98
 
97
99
  // Resolve the schema based on user's anyOf/oneOf tab selections
98
100
  const schema = useMemo(() => {
@@ -316,23 +318,89 @@ function Body({
316
318
  ) {
317
319
  return (
318
320
  <FormItem className="openapi-explorer__form-item-body-container">
319
- {Object.entries(schema.properties ?? {}).map(([key, val]: any) => {
320
- return (
321
- <FormItem
322
- key={key}
323
- label={key}
324
- required={
325
- Array.isArray(schema.required) && schema.required.includes(key)
326
- }
327
- >
328
- <FormBodyItem
329
- schemaObject={val}
330
- id={key}
331
- schema={schema}
332
- ></FormBodyItem>
333
- </FormItem>
334
- );
335
- })}
321
+ <SchemaTabs className="openapi-tabs__schema" lazy>
322
+ {/* @ts-ignore */}
323
+ <TabItem
324
+ label={translate({
325
+ id: OPENAPI_BODY.EXAMPLE_FROM_SCHEMA,
326
+ message: "Example (from schema)",
327
+ })}
328
+ value="Example (from schema)"
329
+ default
330
+ >
331
+ {Object.entries(schema.properties ?? {}).map(([key, val]: any) => {
332
+ return (
333
+ <FormItem key={key}>
334
+ <FormBodyItem
335
+ schemaObject={val}
336
+ id={key}
337
+ schema={schema}
338
+ exampleValue={val.example}
339
+ label={key}
340
+ required={
341
+ Array.isArray(schema.required) &&
342
+ schema.required.includes(key)
343
+ }
344
+ fieldEncoding={encoding?.[key]?.contentType}
345
+ ></FormBodyItem>
346
+ </FormItem>
347
+ );
348
+ })}
349
+ </TabItem>
350
+ {example && (
351
+ // @ts-ignore
352
+ <TabItem label="Example" value="example">
353
+ {Object.entries(schema.properties ?? {}).map(
354
+ ([schemaKey, schemaVal]: any) => {
355
+ return (
356
+ <FormItem key={schemaKey}>
357
+ <FormBodyItem
358
+ schemaObject={schemaVal}
359
+ id={schemaKey}
360
+ schema={schema}
361
+ exampleValue={example[schemaKey]}
362
+ label={schemaKey}
363
+ required={
364
+ Array.isArray(schema.required) &&
365
+ schema.required.includes(schemaKey)
366
+ }
367
+ fieldEncoding={encoding?.[schemaKey]?.contentType}
368
+ ></FormBodyItem>
369
+ </FormItem>
370
+ );
371
+ }
372
+ )}
373
+ </TabItem>
374
+ )}
375
+ {examples &&
376
+ Object.entries(examples).map(([key, value]) => {
377
+ return (
378
+ // @ts-ignore
379
+ <TabItem label={key} value={key} key={key}>
380
+ {Object.entries(schema.properties ?? {}).map(
381
+ ([schemaKey, schemaVal]: any) => {
382
+ return (
383
+ <FormItem key={schemaKey}>
384
+ <FormBodyItem
385
+ schemaObject={schemaVal}
386
+ id={schemaKey}
387
+ schema={schema}
388
+ exampleValue={value.value?.[schemaKey]}
389
+ label={schemaKey}
390
+ required={
391
+ Array.isArray(schema.required) &&
392
+ schema.required.includes(schemaKey)
393
+ }
394
+ fieldEncoding={encoding?.[schemaKey]?.contentType}
395
+ ></FormBodyItem>
396
+ </FormItem>
397
+ );
398
+ }
399
+ )}
400
+ </TabItem>
401
+ );
402
+ })}
403
+ </SchemaTabs>
336
404
  </FormItem>
337
405
  );
338
406
  }
@@ -11,6 +11,7 @@ import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
11
11
  import ApiCodeBlock from "@theme/ApiExplorer/ApiCodeBlock";
12
12
  import buildPostmanRequest from "@theme/ApiExplorer/buildPostmanRequest";
13
13
  import CodeTabs from "@theme/ApiExplorer/CodeTabs";
14
+ import { useResolvedEncoding } from "@theme/ApiExplorer/EncodingSelection/useResolvedEncoding";
14
15
  import { useTypedSelector } from "@theme/ApiItem/hooks";
15
16
  import cloneDeep from "lodash/cloneDeep";
16
17
  import codegen from "postman-code-generators";
@@ -30,6 +31,7 @@ export interface Props {
30
31
  postman: sdk.Request;
31
32
  codeSamples: CodeSample[];
32
33
  maskCredentials?: boolean;
34
+ requestBody?: import("docusaurus-plugin-openapi-docs/src/openapi/types").RequestBodyObject;
33
35
  }
34
36
 
35
37
  function CodeTab({ children, hidden, className }: any): React.JSX.Element {
@@ -44,6 +46,7 @@ function CodeSnippets({
44
46
  postman,
45
47
  codeSamples,
46
48
  maskCredentials: propMaskCredentials,
49
+ requestBody,
47
50
  }: Props) {
48
51
  const { siteConfig } = useDocusaurusContext();
49
52
 
@@ -76,7 +79,9 @@ function CodeSnippets({
76
79
  const authOptions =
77
80
  clonedAuth?.options?.[key] ??
78
81
  clonedAuth?.options?.[comboAuthId];
79
- placeholder = authOptions?.find((opt: any) => opt.key === key)?.name;
82
+ placeholder = authOptions?.find(
83
+ (opt: any) => opt.key === key
84
+ )?.name;
80
85
  obj[key] = cleanCredentials(obj[key]);
81
86
  } else {
82
87
  obj[key] = `<${placeholder ?? key}>`;
@@ -93,6 +98,8 @@ function CodeSnippets({
93
98
  })()
94
99
  : auth;
95
100
 
101
+ const encoding = useResolvedEncoding(requestBody);
102
+
96
103
  // Create a Postman request object using cleanedAuth or original auth
97
104
  const cleanedPostmanRequest = buildPostmanRequest(postman, {
98
105
  queryParams,
@@ -104,6 +111,7 @@ function CodeSnippets({
104
111
  body,
105
112
  server,
106
113
  auth: cleanedAuth,
114
+ encoding,
107
115
  });
108
116
 
109
117
  // User-defined languages array
@@ -9,11 +9,12 @@ import React, { cloneElement, ReactElement, useEffect, useRef } from "react";
9
9
 
10
10
  import {
11
11
  sanitizeTabsChildren,
12
+ type TabItemProps,
12
13
  type TabProps,
14
+ TabsProvider,
13
15
  useScrollPositionBlocker,
14
- useTabs,
16
+ useTabsContextValue,
15
17
  } from "@docusaurus/theme-common/internal";
16
- import { TabItemProps } from "@docusaurus/theme-common/lib/utils/tabsUtils";
17
18
  import useIsBrowser from "@docusaurus/useIsBrowser";
18
19
  import clsx from "clsx";
19
20
 
@@ -43,7 +44,7 @@ function TabList({
43
44
  selectedValue,
44
45
  selectValue,
45
46
  tabValues,
46
- }: CodeTabsProps & ReturnType<typeof useTabs>) {
47
+ }: CodeTabsProps & ReturnType<typeof useTabsContextValue>) {
47
48
  const tabRefs = useRef<(HTMLLIElement | null)[]>([]);
48
49
  const tabsScrollContainerRef = useRef<any>(null);
49
50
  const { blockElementScrollPositionUntilNextRender } =
@@ -192,7 +193,8 @@ function TabContent({
192
193
  lazy,
193
194
  children,
194
195
  selectedValue,
195
- }: CodeTabsProps & ReturnType<typeof useTabs>): React.JSX.Element | null {
196
+ }: CodeTabsProps &
197
+ ReturnType<typeof useTabsContextValue>): React.JSX.Element | null {
196
198
  const childTabs = (Array.isArray(children) ? children : [children]).filter(
197
199
  Boolean
198
200
  ) as ReactElement<TabItemProps>[];
@@ -207,28 +209,26 @@ function TabContent({
207
209
  return cloneElement(selectedTabItem, { className: "margin-top--md" });
208
210
  }
209
211
  return (
210
- <div className="margin-top--md openapi-tabs__code-content">
211
- {childTabs.map((tabItem, i) =>
212
- cloneElement(tabItem, {
213
- key: i,
214
- hidden: tabItem.props.value !== selectedValue,
215
- })
216
- )}
217
- </div>
212
+ <div className="margin-top--md openapi-tabs__code-content">{childTabs}</div>
218
213
  );
219
214
  }
220
215
 
221
216
  function TabsComponent(props: CodeTabsProps & Props): React.JSX.Element {
222
- const tabs = useTabs(props);
217
+ const tabs = useTabsContextValue(props);
223
218
  const { className } = props;
224
219
 
225
220
  return (
226
- <div
227
- className={clsx("tabs-container openapi-tabs__code-container", className)}
228
- >
229
- <TabList {...props} {...tabs} />
230
- <TabContent {...props} {...tabs} />
231
- </div>
221
+ <TabsProvider value={tabs}>
222
+ <div
223
+ className={clsx(
224
+ "tabs-container openapi-tabs__code-container",
225
+ className
226
+ )}
227
+ >
228
+ <TabList {...props} {...tabs} />
229
+ <TabContent {...props} {...tabs} />
230
+ </div>
231
+ </TabsProvider>
232
232
  );
233
233
  }
234
234
 
@@ -12,6 +12,7 @@ import FormSelect from "@theme/ApiExplorer/FormSelect";
12
12
  import { useTypedDispatch, useTypedSelector } from "@theme/ApiItem/hooks";
13
13
 
14
14
  import { setContentType } from "./slice";
15
+ import { clearEncodingSelection } from "@theme/ApiExplorer/EncodingSelection/slice";
15
16
 
16
17
  function ContentType() {
17
18
  const value = useTypedSelector((state: any) => state.contentType.value);
@@ -23,13 +24,15 @@ function ContentType() {
23
24
  }
24
25
 
25
26
  return (
26
- <FormItem label="Content-Type">
27
+ <FormItem>
27
28
  <FormSelect
29
+ label="Content-Type"
28
30
  value={value}
29
31
  options={options}
30
- onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
31
- dispatch(setContentType(e.target.value))
32
- }
32
+ onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
33
+ dispatch(setContentType(e.target.value));
34
+ dispatch(clearEncodingSelection());
35
+ }}
33
36
  />
34
37
  </FormItem>
35
38
  );
@@ -0,0 +1,31 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
9
+
10
+ // Maps form field name → user-selected content type
11
+ export type State = Record<string, string>;
12
+
13
+ const initialState: State = {};
14
+
15
+ export const slice = createSlice({
16
+ name: "encodingSelection",
17
+ initialState,
18
+ reducers: {
19
+ setFieldEncoding: (
20
+ state,
21
+ action: PayloadAction<{ field: string; contentType: string }>
22
+ ) => {
23
+ state[action.payload.field] = action.payload.contentType;
24
+ },
25
+ clearEncodingSelection: () => initialState,
26
+ },
27
+ });
28
+
29
+ export const { setFieldEncoding, clearEncodingSelection } = slice.actions;
30
+
31
+ export default slice.reducer;
@@ -0,0 +1,43 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import { useTypedSelector } from "@theme/ApiItem/hooks";
9
+ import type { RequestBodyObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
10
+
11
+ /**
12
+ * Merges the spec-declared `encoding` for the active content type with any
13
+ * per-field content-type selections the user has made in the UI. User picks
14
+ * take precedence over the spec default.
15
+ *
16
+ * Returns `undefined` when no encoding is declared for the current content
17
+ * type so callers can skip the encoding path entirely.
18
+ */
19
+ export function useResolvedEncoding(
20
+ requestBody: RequestBodyObject | undefined
21
+ ): Record<string, { contentType?: string }> | undefined {
22
+ const contentType = useTypedSelector((state: any) => state.contentType.value);
23
+ const encodingSelection = useTypedSelector(
24
+ (state: any) => state.encodingSelection
25
+ );
26
+
27
+ const specEncoding: Record<string, { contentType?: string }> =
28
+ requestBody?.content?.[contentType]?.encoding ?? {};
29
+
30
+ if (!Object.keys(specEncoding).length) {
31
+ return undefined;
32
+ }
33
+
34
+ return Object.fromEntries(
35
+ Object.entries(specEncoding).map(([field, enc]) => [
36
+ field,
37
+ {
38
+ ...enc,
39
+ contentType: encodingSelection[field] ?? enc.contentType,
40
+ },
41
+ ])
42
+ );
43
+ }
@@ -14,8 +14,3 @@
14
14
  .openapi-explorer__form-item-body-container {
15
15
  padding: 0;
16
16
  }
17
-
18
- .openapi-explorer__form-item-label {
19
- font-family: var(--ifm-font-family-monospace);
20
- font-weight: bold;
21
- }
@@ -7,31 +7,17 @@
7
7
 
8
8
  import React from "react";
9
9
 
10
- import { translate } from "@docusaurus/Translate";
11
- import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
12
10
  import clsx from "clsx";
13
11
 
14
12
  export interface Props {
15
- label?: string;
16
- type?: string;
17
- required?: boolean | undefined;
18
13
  children?: React.ReactNode;
19
14
  className?: string;
20
15
  }
21
16
 
22
- function FormItem({ label, type, required, children, className }: Props) {
17
+ function FormItem({ children, className }: Props) {
23
18
  return (
24
19
  <div className={clsx("openapi-explorer__form-item", className)}>
25
- {label && (
26
- <label className="openapi-explorer__form-item-label">{label}</label>
27
- )}
28
- {type && <span style={{ opacity: 0.6 }}> — {type}</span>}
29
- {required && (
30
- <span className="openapi-schema__required">
31
- {translate({ id: OPENAPI_SCHEMA_ITEM.REQUIRED, message: "required" })}
32
- </span>
33
- )}
34
- <div>{children}</div>
20
+ {children}
35
21
  </div>
36
22
  );
37
23
  }
@@ -0,0 +1,4 @@
1
+ .openapi-explorer__form-item-label {
2
+ font-family: var(--ifm-font-family-monospace);
3
+ font-weight: bold;
4
+ }
@@ -0,0 +1,43 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import React from "react";
9
+
10
+ import { translate } from "@docusaurus/Translate";
11
+ import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
12
+
13
+ export interface Props {
14
+ htmlFor?: string;
15
+ label: string;
16
+ type?: string;
17
+ required?: boolean;
18
+ }
19
+
20
+ function FormLabel({ htmlFor, label, type, required }: Props) {
21
+ return (
22
+ <>
23
+ {htmlFor ? (
24
+ <label className="openapi-explorer__form-item-label" htmlFor={htmlFor}>
25
+ {label}
26
+ </label>
27
+ ) : (
28
+ <span className="openapi-explorer__form-item-label">{label}</span>
29
+ )}
30
+ {type && <span style={{ opacity: 0.6 }}> — {type}</span>}
31
+ {required && (
32
+ <span className="openapi-schema__required">
33
+ {translate({
34
+ id: OPENAPI_SCHEMA_ITEM.REQUIRED,
35
+ message: "required",
36
+ })}
37
+ </span>
38
+ )}
39
+ </>
40
+ );
41
+ }
42
+
43
+ export default FormLabel;
@@ -5,8 +5,9 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  * ========================================================================== */
7
7
 
8
- import React from "react";
8
+ import React, { useId } from "react";
9
9
 
10
+ import FormLabel from "@theme/ApiExplorer/FormLabel";
10
11
  import clsx from "clsx";
11
12
 
12
13
  export interface Props {
@@ -14,9 +15,22 @@ export interface Props {
14
15
  options: string[];
15
16
  onChange?: React.ChangeEventHandler<HTMLSelectElement>;
16
17
  showErrors?: boolean;
18
+ label?: string;
19
+ type?: string;
20
+ required?: boolean;
17
21
  }
18
22
 
19
- function FormMultiSelect({ value, options, onChange, showErrors }: Props) {
23
+ function FormMultiSelect({
24
+ value,
25
+ options,
26
+ onChange,
27
+ showErrors,
28
+ label,
29
+ type,
30
+ required,
31
+ }: Props) {
32
+ const id = useId();
33
+
20
34
  if (options.length === 0) {
21
35
  return null;
22
36
  }
@@ -33,24 +47,30 @@ function FormMultiSelect({ value, options, onChange, showErrors }: Props) {
33
47
  }
34
48
 
35
49
  return (
36
- <select
37
- style={{ height: height }}
38
- className={clsx("openapi-explorer__multi-select-input", {
39
- error: showErrors,
40
- })}
41
- value={value}
42
- onChange={onChange}
43
- size={Math.min(6, options.length + 1)}
44
- multiple
45
- >
46
- {options.map((option) => {
47
- return (
48
- <option key={option} value={option}>
49
- {option}
50
- </option>
51
- );
52
- })}
53
- </select>
50
+ <>
51
+ {label && (
52
+ <FormLabel htmlFor={id} label={label} type={type} required={required} />
53
+ )}
54
+ <select
55
+ id={label ? id : undefined}
56
+ style={{ height: height }}
57
+ className={clsx("openapi-explorer__multi-select-input", {
58
+ error: showErrors,
59
+ })}
60
+ value={value}
61
+ onChange={onChange}
62
+ size={Math.min(6, options.length + 1)}
63
+ multiple
64
+ >
65
+ {options.map((option) => {
66
+ return (
67
+ <option key={option} value={option}>
68
+ {option}
69
+ </option>
70
+ );
71
+ })}
72
+ </select>
73
+ </>
54
74
  );
55
75
  }
56
76
 
@@ -5,33 +5,59 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  * ========================================================================== */
7
7
 
8
- import React from "react";
8
+ import React, { useId } from "react";
9
+
10
+ import FormLabel from "@theme/ApiExplorer/FormLabel";
9
11
 
10
12
  export interface Props {
11
13
  value?: string;
12
14
  options?: string[];
13
15
  onChange?: React.ChangeEventHandler<HTMLSelectElement>;
16
+ label?: string;
17
+ type?: string;
18
+ required?: boolean;
19
+ ariaLabelledBy?: string;
20
+ ariaLabel?: string;
14
21
  }
15
22
 
16
- function FormSelect({ value, options, onChange }: Props) {
23
+ function FormSelect({
24
+ value,
25
+ options,
26
+ onChange,
27
+ label,
28
+ type,
29
+ required,
30
+ ariaLabelledBy,
31
+ ariaLabel,
32
+ }: Props) {
33
+ const id = useId();
34
+
17
35
  if (!Array.isArray(options) || options.length === 0) {
18
36
  return null;
19
37
  }
20
38
 
21
39
  return (
22
- <select
23
- className="openapi-explorer__select-input"
24
- value={value}
25
- onChange={onChange}
26
- >
27
- {options.map((option) => {
28
- return (
29
- <option key={option} value={option}>
30
- {option}
31
- </option>
32
- );
33
- })}
34
- </select>
40
+ <>
41
+ {label && (
42
+ <FormLabel htmlFor={id} label={label} type={type} required={required} />
43
+ )}
44
+ <select
45
+ id={label ? id : undefined}
46
+ className="openapi-explorer__select-input"
47
+ value={value}
48
+ onChange={onChange}
49
+ aria-labelledby={!label ? ariaLabelledBy : undefined}
50
+ aria-label={!label && !ariaLabelledBy ? ariaLabel : undefined}
51
+ >
52
+ {options.map((option) => {
53
+ return (
54
+ <option key={option} value={option}>
55
+ {option}
56
+ </option>
57
+ );
58
+ })}
59
+ </select>
60
+ </>
35
61
  );
36
62
  }
37
63
 
@@ -6,10 +6,11 @@
6
6
  * ========================================================================== */
7
7
 
8
8
  // @ts-nocheck
9
- import React from "react";
9
+ import React, { useId } from "react";
10
10
 
11
11
  import { translate } from "@docusaurus/Translate";
12
12
  import { ErrorMessage } from "@hookform/error-message";
13
+ import FormLabel from "@theme/ApiExplorer/FormLabel";
13
14
  import { OPENAPI_FORM } from "@theme/translationIds";
14
15
  import clsx from "clsx";
15
16
  import { useFormContext } from "react-hook-form";
@@ -21,6 +22,9 @@ export interface Props {
21
22
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
22
23
  paramName?: string;
23
24
  isRequired?: boolean;
25
+ label?: string;
26
+ type?: string;
27
+ required?: boolean;
24
28
  }
25
29
 
26
30
  function FormTextInput({
@@ -30,7 +34,12 @@ function FormTextInput({
30
34
  password,
31
35
  onChange,
32
36
  paramName,
37
+ label,
38
+ type,
39
+ required,
33
40
  }: Props) {
41
+ const id = useId();
42
+
34
43
  placeholder = placeholder?.split("\n")[0];
35
44
 
36
45
  const {
@@ -42,6 +51,9 @@ function FormTextInput({
42
51
 
43
52
  return (
44
53
  <>
54
+ {label && (
55
+ <FormLabel htmlFor={id} label={label} type={type} required={required} />
56
+ )}
45
57
  {paramName ? (
46
58
  <input
47
59
  {...register(paramName, {
@@ -52,6 +64,7 @@ function FormTextInput({
52
64
  })
53
65
  : false,
54
66
  })}
67
+ id={label ? id : undefined}
55
68
  className={clsx("openapi-explorer__form-item-input", {
56
69
  error: showErrorMessage,
57
70
  })}
@@ -64,6 +77,7 @@ function FormTextInput({
64
77
  />
65
78
  ) : (
66
79
  <input
80
+ id={label ? id : undefined}
67
81
  className="openapi-explorer__form-item-input"
68
82
  type={password ? "password" : "text"}
69
83
  placeholder={placeholder}