@digi-frontend/dgate-api-documentation 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/index3.js +1 -1
- package/dist/_virtual/index4.js +1 -1
- package/dist/_virtual/index5.js +1 -1
- package/dist/_virtual/index6.js +1 -1
- package/dist/node_modules/toposort/index.js +1 -1
- package/dist/node_modules/yup/index.esm.js +1 -1
- package/dist/src/components/InfoForm/InfoForm.js.map +1 -1
- package/dist/src/components/JsonInput/JsonInput.js +1 -1
- package/dist/src/components/JsonInput/JsonInput.js.map +1 -1
- package/dist/src/components/LivePreview/LivePreview.js +1 -1
- package/dist/src/components/LivePreview/LivePreview.js.map +1 -1
- package/dist/src/components/MethodAccordion/MethodAccordion.js +1 -1
- package/dist/src/components/MethodAccordion/MethodAccordion.js.map +1 -1
- package/dist/src/components/table/table.js +1 -1
- package/dist/src/components/table/table.js.map +1 -1
- package/dist/src/constants/index.js.map +1 -1
- package/dist/src/helpers/layout.helper.js +1 -1
- package/dist/src/helpers/layout.helper.js.map +1 -1
- package/dist/src/layout/layout.js +1 -1
- package/dist/src/layout/layout.js.map +1 -1
- package/dist/src/layout/layout.module.css.js +1 -1
- package/dist/styles.css +537 -522
- package/dist/types/components/JsonInput/JsonInput.d.ts +2 -1
- package/dist/types/components/MethodAccordion/MethodAccordion.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/InfoForm/InfoForm.tsx +1 -0
- package/src/components/JsonInput/JsonInput.tsx +34 -26
- package/src/components/JsonInput/style.module.scss +14 -4
- package/src/components/LivePreview/LivePreview.tsx +11 -17
- package/src/components/MethodAccordion/MethodAccordion.module.scss +23 -10
- package/src/components/MethodAccordion/MethodAccordion.tsx +7 -4
- package/src/components/table/table.tsx +27 -16
- package/src/constants/index.ts +4 -4
- package/src/helpers/layout.helper.ts +13 -1
- package/src/layout/layout.module.css +1 -1
- package/src/layout/layout.tsx +1 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const JsonInput: ({ placeholder, label, value, onValidation, onChange, dataId, className, errorMessage, acceptType, fieldIsDisabled, }: {
|
|
1
|
+
declare const JsonInput: ({ placeholder, label, value, onValidation, onChange, dataId, className, errorMessage, acceptType, fieldIsDisabled, withFooter, }: {
|
|
2
2
|
placeholder: any;
|
|
3
3
|
label: any;
|
|
4
4
|
value: any;
|
|
@@ -9,5 +9,6 @@ declare const JsonInput: ({ placeholder, label, value, onValidation, onChange, d
|
|
|
9
9
|
errorMessage: any;
|
|
10
10
|
acceptType?: string;
|
|
11
11
|
fieldIsDisabled: any;
|
|
12
|
+
withFooter?: boolean;
|
|
12
13
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
13
14
|
export default JsonInput;
|
|
@@ -2,7 +2,7 @@ import { TransformedMethod } from '../../types/layout.type';
|
|
|
2
2
|
declare const MethodsAccordion: ({ method, path, setFieldValue, readOnly, }: {
|
|
3
3
|
method: TransformedMethod;
|
|
4
4
|
path: string;
|
|
5
|
-
setFieldValue
|
|
5
|
+
setFieldValue?: (key: string, value: string) => void;
|
|
6
6
|
readOnly?: boolean;
|
|
7
7
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export default MethodsAccordion;
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import SVGLoader from '../../components/SVGLoader/SVGLoader'
|
|
|
11
11
|
import CommonDialog from '../../components/dialog'
|
|
12
12
|
import { tagsTableHeaders } from '../../constants/index'
|
|
13
13
|
import TagsTable from '../../components/table/tags-table'
|
|
14
|
+
|
|
14
15
|
const InfoForm = ({ readOnly }: { readOnly?: boolean }) => {
|
|
15
16
|
const [tableData, setTableData] = useState([])
|
|
16
17
|
const [tableRecords, setTableRecords] = useState()
|
|
@@ -13,6 +13,7 @@ const JsonInput = ({
|
|
|
13
13
|
errorMessage,
|
|
14
14
|
acceptType = 'BOTH',
|
|
15
15
|
fieldIsDisabled,
|
|
16
|
+
withFooter = true,
|
|
16
17
|
}) => {
|
|
17
18
|
const [isValid, setIsValid] = useState()
|
|
18
19
|
const [disabledBeautify, setDisabledBeautify] = useState(true)
|
|
@@ -69,7 +70,11 @@ const JsonInput = ({
|
|
|
69
70
|
}, [])
|
|
70
71
|
|
|
71
72
|
return (
|
|
72
|
-
<div
|
|
73
|
+
<div
|
|
74
|
+
className={`${styles['json-editor-container']} ${className} ${
|
|
75
|
+
fieldIsDisabled ? styles.disabled : ''
|
|
76
|
+
}`}
|
|
77
|
+
>
|
|
73
78
|
{label && <p className={`${styles['json-editor-label']}`}>{label}</p>}
|
|
74
79
|
<div
|
|
75
80
|
className={`${styles['json-editor']} ${
|
|
@@ -77,6 +82,7 @@ const JsonInput = ({
|
|
|
77
82
|
}`}
|
|
78
83
|
>
|
|
79
84
|
<textarea
|
|
85
|
+
disabled={fieldIsDisabled}
|
|
80
86
|
data-id={dataId}
|
|
81
87
|
value={value}
|
|
82
88
|
onChange={(e) => {
|
|
@@ -94,31 +100,33 @@ const JsonInput = ({
|
|
|
94
100
|
min="1"
|
|
95
101
|
placeholder={placeholder}
|
|
96
102
|
/>
|
|
97
|
-
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
103
|
+
{withFooter && (
|
|
104
|
+
<div className={`${styles['actions-container']}`}>
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
className={styles.validate}
|
|
108
|
+
onClick={() => {
|
|
109
|
+
const valid = checkIsValid()
|
|
110
|
+
setIsValid(valid)
|
|
111
|
+
onValidation(valid)
|
|
112
|
+
setDisabledBeautify(!checkIsValid())
|
|
113
|
+
}}
|
|
114
|
+
data-id={`${dataId}-VALIDATE-BUTTON`}
|
|
115
|
+
disabled={value === ''}
|
|
116
|
+
>
|
|
117
|
+
Validate
|
|
118
|
+
</button>
|
|
119
|
+
<button
|
|
120
|
+
type="button"
|
|
121
|
+
disabled={value === '' || disabledBeautify}
|
|
122
|
+
data-id={`${dataId}-BEAUTIFY-BUTTON`}
|
|
123
|
+
className={styles.beautify}
|
|
124
|
+
onClick={handlePrettify}
|
|
125
|
+
>
|
|
126
|
+
Beautify
|
|
127
|
+
</button>
|
|
128
|
+
</div>
|
|
129
|
+
)}
|
|
122
130
|
</div>
|
|
123
131
|
{(errorMessage || (value !== '' && isValid === false)) && (
|
|
124
132
|
<p className={styles['error-message']}>{errorMessage || 'Invalid JSON or YAML format'}</p>
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
.json-editor-container {
|
|
2
|
+
overflow: hidden;
|
|
3
|
+
border-bottom-right-radius: 0.3125rem;
|
|
4
|
+
border-bottom-left-radius: 0.3125rem;
|
|
5
|
+
|
|
2
6
|
.json-editor-label {
|
|
3
7
|
font-size: 0.875rem;
|
|
4
8
|
font-weight: 600;
|
|
@@ -11,10 +15,9 @@
|
|
|
11
15
|
flex-direction: column;
|
|
12
16
|
background-color: #142452;
|
|
13
17
|
width: 100%;
|
|
14
|
-
border-radius: 0.3125rem;
|
|
15
|
-
margin-bottom: -0.5rem;
|
|
16
18
|
outline: 1px solid transparent;
|
|
17
19
|
position: relative;
|
|
20
|
+
overflow: hidden;
|
|
18
21
|
|
|
19
22
|
.actions-container {
|
|
20
23
|
width: 100%;
|
|
@@ -80,7 +83,6 @@
|
|
|
80
83
|
background-color: #142452;
|
|
81
84
|
border: none;
|
|
82
85
|
resize: none;
|
|
83
|
-
border-radius: 0.3125rem;
|
|
84
86
|
|
|
85
87
|
&:focus,
|
|
86
88
|
&:focus-within,
|
|
@@ -97,7 +99,6 @@
|
|
|
97
99
|
background-color: #828699;
|
|
98
100
|
border: none;
|
|
99
101
|
border-color: transparent;
|
|
100
|
-
border-top-right-radius: 0.25rem;
|
|
101
102
|
border-top-left-radius: 0;
|
|
102
103
|
}
|
|
103
104
|
|
|
@@ -120,4 +121,13 @@
|
|
|
120
121
|
line-height: 1.25rem;
|
|
121
122
|
margin-top: 0.625rem;
|
|
122
123
|
}
|
|
124
|
+
|
|
125
|
+
&.disabled {
|
|
126
|
+
.json-editor {
|
|
127
|
+
textarea {
|
|
128
|
+
background-color: white;
|
|
129
|
+
color: #12131a;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
123
133
|
}
|
|
@@ -5,14 +5,15 @@ import Chips from '../../components/Chips/Chips'
|
|
|
5
5
|
import MethodsAccordion from '../../components/MethodAccordion/MethodAccordion'
|
|
6
6
|
import { TransformedOpenApi } from '../../types/transformedOpenApi'
|
|
7
7
|
import { useFormikContext } from 'formik'
|
|
8
|
+
import { methodColorMapping } from '../../constants/index'
|
|
8
9
|
|
|
9
10
|
interface LivePreviewProps {
|
|
10
11
|
transformedData?: TransformedOpenApi
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
const LivePreview: React.FC<LivePreviewProps> = ({ transformedData }) => {
|
|
14
|
-
const { values
|
|
15
|
-
const { info, components, tags
|
|
15
|
+
const { values } = useFormikContext<TransformedOpenApi>()
|
|
16
|
+
const { info, components, tags } = values
|
|
16
17
|
const { securitySchemes } = components
|
|
17
18
|
const securityKey =
|
|
18
19
|
securitySchemes && typeof securitySchemes == 'object' && Object.keys(securitySchemes).length
|
|
@@ -55,21 +56,14 @@ const LivePreview: React.FC<LivePreviewProps> = ({ transformedData }) => {
|
|
|
55
56
|
/>
|
|
56
57
|
|
|
57
58
|
<SimpleLabelValue key={'endpoints'} label={'Endpoints '} />
|
|
58
|
-
{values.paths.map((path
|
|
59
|
-
|
|
60
|
-
{path.methods
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
method={method}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const h = `paths[${pathIndex}].methods[${methodIndex}].${key}`
|
|
67
|
-
setFieldValue(h, value)
|
|
68
|
-
}}
|
|
69
|
-
handleSave={()=>null}
|
|
70
|
-
/>
|
|
71
|
-
))}
|
|
72
|
-
</>
|
|
59
|
+
{values.paths.map((path) => (
|
|
60
|
+
<div className={styles.methodsContainer}>
|
|
61
|
+
{path.methods
|
|
62
|
+
.sort((a, b) => methodColorMapping[a.type].order + methodColorMapping[b.type].order)
|
|
63
|
+
.map((method, methodIndex) => (
|
|
64
|
+
<MethodsAccordion readOnly method={method} path={path.path} />
|
|
65
|
+
))}
|
|
66
|
+
</div>
|
|
73
67
|
))}
|
|
74
68
|
</div>
|
|
75
69
|
</div>
|
|
@@ -71,16 +71,6 @@
|
|
|
71
71
|
transition: rotate 0.3s ease-in-out;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
&.readOnly {
|
|
76
|
-
.methodSummary {
|
|
77
|
-
background-color: #ebecf2;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.methodExpandArrowContainer {
|
|
81
|
-
background-color: #ebecf2;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
74
|
}
|
|
85
75
|
|
|
86
76
|
.methodAccordionContent {
|
|
@@ -309,4 +299,27 @@
|
|
|
309
299
|
}
|
|
310
300
|
}
|
|
311
301
|
}
|
|
302
|
+
|
|
303
|
+
&.readOnly {
|
|
304
|
+
.methodAccordionContent {
|
|
305
|
+
padding: 0;
|
|
306
|
+
}
|
|
307
|
+
:global(.accordion-header) {
|
|
308
|
+
margin: 1.25rem 0 0 0 !important;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
:global(.accordion-details) {
|
|
312
|
+
background-color: transparent !important;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.methodSummaryContainer {
|
|
316
|
+
.methodSummary {
|
|
317
|
+
background-color: #ebecf2;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.methodExpandArrowContainer {
|
|
321
|
+
background-color: #ebecf2;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
312
325
|
}
|
|
@@ -39,7 +39,7 @@ const MethodsAccordion = ({
|
|
|
39
39
|
}: {
|
|
40
40
|
method: TransformedMethod
|
|
41
41
|
path: string
|
|
42
|
-
setFieldValue
|
|
42
|
+
setFieldValue?: (key: string, value: string) => void
|
|
43
43
|
readOnly?: boolean
|
|
44
44
|
}) => {
|
|
45
45
|
const [isExpanded, setIsExpanded] = useState({
|
|
@@ -218,9 +218,9 @@ const MethodsAccordion = ({
|
|
|
218
218
|
<Accordion
|
|
219
219
|
expanded={isExpanded.method}
|
|
220
220
|
onChange={() => setIsExpanded((prev) => ({ ...prev, method: !prev.method }))}
|
|
221
|
-
className={styles.methodAccordion}
|
|
221
|
+
className={`${styles.methodAccordion} ${readOnly ? styles.readOnly : ''}`}
|
|
222
222
|
summary={
|
|
223
|
-
<div className={
|
|
223
|
+
<div className={styles.methodSummaryContainer}>
|
|
224
224
|
<div className={styles.methodSummary}>
|
|
225
225
|
<span
|
|
226
226
|
style={{
|
|
@@ -247,7 +247,7 @@ const MethodsAccordion = ({
|
|
|
247
247
|
<TextArea
|
|
248
248
|
className={styles.methodDesc}
|
|
249
249
|
label="Description"
|
|
250
|
-
placeholder="Describe the
|
|
250
|
+
placeholder="Describe the method's purpose and functionality..."
|
|
251
251
|
value={method?.description}
|
|
252
252
|
onChange={(value) => setFieldValue('description', value)}
|
|
253
253
|
/>
|
|
@@ -299,6 +299,7 @@ const MethodsAccordion = ({
|
|
|
299
299
|
}
|
|
300
300
|
children={
|
|
301
301
|
<JsonInput
|
|
302
|
+
withFooter={!readOnly}
|
|
302
303
|
className={'jsonField'}
|
|
303
304
|
placeholder="Enter your request body as a JSON object...."
|
|
304
305
|
fieldIsDisabled={readOnly}
|
|
@@ -340,6 +341,7 @@ const MethodsAccordion = ({
|
|
|
340
341
|
withSearch={false}
|
|
341
342
|
isMultiple={false}
|
|
342
343
|
clearable={false}
|
|
344
|
+
disabled={readOnly}
|
|
343
345
|
placeholder="200"
|
|
344
346
|
options={[
|
|
345
347
|
{
|
|
@@ -355,6 +357,7 @@ const MethodsAccordion = ({
|
|
|
355
357
|
}
|
|
356
358
|
children={
|
|
357
359
|
<JsonInput
|
|
360
|
+
withFooter={!readOnly}
|
|
358
361
|
className={'jsonField'}
|
|
359
362
|
placeholder="Enter your response as a JSON object..."
|
|
360
363
|
fieldIsDisabled={readOnly}
|
|
@@ -46,6 +46,7 @@ const ParamterTable = ({
|
|
|
46
46
|
resetForm()
|
|
47
47
|
},
|
|
48
48
|
})
|
|
49
|
+
|
|
49
50
|
return (
|
|
50
51
|
<div className="tableSectionContainer">
|
|
51
52
|
<div className="tableContainer">
|
|
@@ -105,12 +106,22 @@ const ParamterTable = ({
|
|
|
105
106
|
</tr>
|
|
106
107
|
)
|
|
107
108
|
})}
|
|
108
|
-
{
|
|
109
|
-
<tr
|
|
109
|
+
{data?.length === 0 && readOnly && (
|
|
110
|
+
<tr className="fallbackTableRow">
|
|
111
|
+
<td className="fallbackContainer" colSpan={headCells?.length}>
|
|
112
|
+
<div className="fallbackTextContainer">
|
|
113
|
+
<span className="fallbackText"> No Data Available</span>
|
|
114
|
+
</div>
|
|
115
|
+
</td>
|
|
116
|
+
</tr>
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
{isFormOpen && !readOnly ? (
|
|
120
|
+
<tr className={`row`}>
|
|
110
121
|
<td key={'Parameter name'}>
|
|
111
122
|
<div data-id="TEXT_DESCRIPTION" className="tableData">
|
|
112
123
|
<Input
|
|
113
|
-
placeholder="
|
|
124
|
+
placeholder="Parameter name"
|
|
114
125
|
size="large"
|
|
115
126
|
type="text"
|
|
116
127
|
// errorMsg={!!errors.name && errors.name}
|
|
@@ -154,6 +165,7 @@ const ParamterTable = ({
|
|
|
154
165
|
<div data-id="TEXT_DESCRIPTION" className="tableData">
|
|
155
166
|
<SelectGroupV2
|
|
156
167
|
disabled={readOnly}
|
|
168
|
+
clearable={false}
|
|
157
169
|
value={{
|
|
158
170
|
label: capitalize(values.schema.type),
|
|
159
171
|
value: capitalize(values.schema.type),
|
|
@@ -180,20 +192,19 @@ const ParamterTable = ({
|
|
|
180
192
|
|
|
181
193
|
<td key={'parameter required'}>
|
|
182
194
|
<div data-id="is required" className="tableData">
|
|
183
|
-
{
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
:
|
|
195
|
+
{readOnly ? (
|
|
196
|
+
<>{values.required ? 'True' : 'False'}</>
|
|
197
|
+
) : (
|
|
187
198
|
<Switch
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
199
|
+
checked={values.required}
|
|
200
|
+
onClick={() => {
|
|
201
|
+
if (readOnly) {
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
setFieldValue('required', !values.required)
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
)}
|
|
197
208
|
</div>
|
|
198
209
|
</td>
|
|
199
210
|
|
package/src/constants/index.ts
CHANGED
|
@@ -46,20 +46,20 @@ export const tagsTableHeaders = [
|
|
|
46
46
|
id: 'tagName',
|
|
47
47
|
label: 'Tag Name',
|
|
48
48
|
sortable: false,
|
|
49
|
-
classes:'requiredParam'
|
|
49
|
+
classes: 'requiredParam',
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
id: 'description',
|
|
53
53
|
label: 'Description',
|
|
54
54
|
sortable: false,
|
|
55
|
-
classes:'requiredParam'
|
|
55
|
+
classes: 'requiredParam',
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
58
|
id: 'externalDocs',
|
|
59
59
|
label: 'External Docs',
|
|
60
60
|
sortable: false,
|
|
61
|
-
classes:'requiredParam'
|
|
62
|
-
}
|
|
61
|
+
classes: 'requiredParam',
|
|
62
|
+
},
|
|
63
63
|
]
|
|
64
64
|
|
|
65
65
|
export const paramsTableHeaders = [
|
|
@@ -5,13 +5,21 @@ import { TransformedOpenApi } from '@entities/transformedOpenApi'
|
|
|
5
5
|
export const transformOpenApiObject = (openApiJson: OpenAPIFile): TransformedOpenApi => {
|
|
6
6
|
if (openApiJson.components && openApiJson.components.securitySchemes) {
|
|
7
7
|
const authKey = Object.keys(openApiJson.components.securitySchemes)?.at(0)
|
|
8
|
-
|
|
9
8
|
if (authKey) {
|
|
10
9
|
openApiJson.components.securitySchemes[authKey].type =
|
|
11
10
|
openApiJson?.components?.securitySchemes?.[
|
|
12
11
|
authKey
|
|
13
12
|
]?.type?.toUpperCase() as SecurityScheme['type']
|
|
14
13
|
}
|
|
14
|
+
} else {
|
|
15
|
+
openApiJson.components = {
|
|
16
|
+
securitySchemes: {
|
|
17
|
+
Public: {
|
|
18
|
+
type: 'http',
|
|
19
|
+
scheme: 'public',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
}
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
return {
|
|
@@ -63,6 +71,10 @@ export const transformPathsToArray = (paths: OpenAPIFile['paths']): TransformedP
|
|
|
63
71
|
}
|
|
64
72
|
}),
|
|
65
73
|
}
|
|
74
|
+
// Add parameters if it does not exist in the original JSON
|
|
75
|
+
if (!methodProps?.parameters) {
|
|
76
|
+
obj.parameters = []
|
|
77
|
+
}
|
|
66
78
|
|
|
67
79
|
if (method.toLowerCase() != 'get') {
|
|
68
80
|
const contentType = Object.keys(methodProps?.requestBody?.content || {})[0]
|
package/src/layout/layout.tsx
CHANGED
|
@@ -68,8 +68,7 @@ const Layout = ({ openApiJson, handleSave }: ILayoutProps): JSX.Element => {
|
|
|
68
68
|
<>
|
|
69
69
|
{path.methods
|
|
70
70
|
.sort(
|
|
71
|
-
(
|
|
72
|
-
methodColorMapping[method.type].order + methodColorMapping[method.type].order
|
|
71
|
+
(a, b) => methodColorMapping[a.type].order - methodColorMapping[b.type].order
|
|
73
72
|
)
|
|
74
73
|
.map((method, methodIndex) => (
|
|
75
74
|
<MethodsAccordion
|