mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-08 14:14:56 +03:00
Merge branch 'master-with-console-response' into 'master-with-console'
Master with console response See merge request babel/babel-api-doc!3
This commit is contained in:
commit
89e8dc8dcb
|
@ -4,6 +4,7 @@ import { SubmitButton } from '../../common-elements/buttons';
|
||||||
import { FlexLayoutReverse } from '../../common-elements/panels';
|
import { FlexLayoutReverse } from '../../common-elements/panels';
|
||||||
import { FieldModel, OperationModel } from '../../services/models';
|
import { FieldModel, OperationModel } from '../../services/models';
|
||||||
import { OpenAPISchema } from '../../types';
|
import { OpenAPISchema } from '../../types';
|
||||||
|
import { ConsoleResponse } from '../ConsoleResponse/Response';
|
||||||
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
|
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
|
||||||
import { ConsoleEditor } from './ConsoleEditor';
|
import { ConsoleEditor } from './ConsoleEditor';
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ export interface ConsoleViewerProps {
|
||||||
additionalHeaders?: object;
|
additionalHeaders?: object;
|
||||||
queryParamPrefix?: string;
|
queryParamPrefix?: string;
|
||||||
queryParamSuffix?: string;
|
queryParamSuffix?: string;
|
||||||
|
urlIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConsoleViewerState {
|
export interface ConsoleViewerState {
|
||||||
|
@ -47,7 +49,7 @@ export class ConsoleViewer extends React.Component<ConsoleViewerProps, ConsoleVi
|
||||||
const mediaType = content && content.mediaTypes[content.activeMimeIdx];
|
const mediaType = content && content.mediaTypes[content.activeMimeIdx];
|
||||||
const endpoint = {
|
const endpoint = {
|
||||||
method: operation.httpVerb,
|
method: operation.httpVerb,
|
||||||
path: operation.servers[0].url + operation.path,
|
path: operation.servers[this.props.urlIndex].url + operation.path,
|
||||||
};
|
};
|
||||||
if (value) {
|
if (value) {
|
||||||
value = JSON.parse(value);
|
value = JSON.parse(value);
|
||||||
|
@ -168,7 +170,7 @@ export class ConsoleViewer extends React.Component<ConsoleViewerProps, ConsoleVi
|
||||||
<SubmitButton onClick={this.onClickSend} >Send Request</SubmitButton>
|
<SubmitButton onClick={this.onClickSend} >Send Request</SubmitButton>
|
||||||
</FlexLayoutReverse>
|
</FlexLayoutReverse>
|
||||||
{result &&
|
{result &&
|
||||||
<SourceCodeWithCopy lang="json" source={JSON.stringify(result, null, 2)} />
|
<ConsoleResponse response={result} />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
114
src/components/ConsoleResponse/Response.tsx
Normal file
114
src/components/ConsoleResponse/Response.tsx
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { SourceCodeWithCopy } from '..';
|
||||||
|
import { RightPanelHeader } from '../../common-elements';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
|
import { JsonViewer } from '../JsonViewer/JsonViewer';
|
||||||
|
|
||||||
|
interface ConsoleResponseProps {
|
||||||
|
response: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConsoleResponseState {
|
||||||
|
collapse: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ConsoleResponse extends React.PureComponent<ConsoleResponseProps, ConsoleResponseState> {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = { collapse: false};
|
||||||
|
}
|
||||||
|
|
||||||
|
changeCollapse = () => {
|
||||||
|
this.setState({collapse: !this.state.collapse});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return(
|
||||||
|
<>
|
||||||
|
<RightPanelHeader> status: </RightPanelHeader>
|
||||||
|
<StatusWrapper className={'status-' + this.props.response.type}> {this.props.response.status} {this.props.response.statusText}</StatusWrapper>
|
||||||
|
<RightPanelHeader> Response Payload </RightPanelHeader>
|
||||||
|
<JsonWrapper>
|
||||||
|
<JsonViewer data={this.props.response.content!} />
|
||||||
|
</JsonWrapper>
|
||||||
|
<RightPanelHeader> Response Headers</RightPanelHeader>
|
||||||
|
<HeaderWrapper>
|
||||||
|
<SourceCodeWrapper className={'collapse-' + this.state.collapse}>
|
||||||
|
<SourceCodeWithCopy lang="json" source={JSON.stringify(this.props.response.headers, null, 2)}/>
|
||||||
|
</SourceCodeWrapper>
|
||||||
|
<ShowMore onClick={this.changeCollapse}>
|
||||||
|
<u>+ show undocumented response headers</u>
|
||||||
|
</ShowMore>
|
||||||
|
</HeaderWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const HeaderWrapper = styled.div`
|
||||||
|
color: white;
|
||||||
|
background-color: ${props => props.theme.codeSample.backgroundColor};
|
||||||
|
padding: 10px 0 18px;
|
||||||
|
margin: 10px 0;
|
||||||
|
height: 100%;
|
||||||
|
div div div {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
div pre span:first-child {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
div pre span:last-child {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
div pre {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SourceCodeWrapper = styled.div`
|
||||||
|
&.collapse-false {
|
||||||
|
height: 89px;
|
||||||
|
}
|
||||||
|
&.collapse-true {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const JsonWrapper = styled.div`
|
||||||
|
color: white;
|
||||||
|
background-color: ${props => props.theme.codeSample.backgroundColor};
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StatusWrapper = styled.div`
|
||||||
|
&.status-success {
|
||||||
|
color: #00ff1c;
|
||||||
|
}
|
||||||
|
&.status-redirect {
|
||||||
|
color: ${props => props.theme.colors.responses.redirect.color};
|
||||||
|
}
|
||||||
|
&.status-info {
|
||||||
|
color: ${props => props.theme.colors.responses.info.color};
|
||||||
|
}
|
||||||
|
&.status-error {
|
||||||
|
color: ${props => props.theme.colors.responses.error.color};
|
||||||
|
}
|
||||||
|
color: white;
|
||||||
|
background-color: ${props => props.theme.codeSample.backgroundColor};
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ShowMore = styled.div`
|
||||||
|
text-align: center;
|
||||||
|
u {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ShelfIcon } from '../../common-elements';
|
import { ShelfIcon } from '../../common-elements';
|
||||||
import { OperationModel } from '../../services';
|
import { ClipboardService, OperationModel } from '../../services';
|
||||||
import { Markdown } from '../Markdown/Markdown';
|
import { Markdown } from '../Markdown/Markdown';
|
||||||
import { OptionsContext } from '../OptionsProvider';
|
import { OptionsContext } from '../OptionsProvider';
|
||||||
import { SelectOnClick } from '../SelectOnClick/SelectOnClick';
|
import { SelectOnClick } from '../SelectOnClick/SelectOnClick';
|
||||||
|
@ -21,10 +21,13 @@ export interface EndpointProps {
|
||||||
|
|
||||||
hideHostname?: boolean;
|
hideHostname?: boolean;
|
||||||
inverted?: boolean;
|
inverted?: boolean;
|
||||||
|
handleUrl: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EndpointState {
|
export interface EndpointState {
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
|
selectedItem: number;
|
||||||
|
tooltipShown: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
||||||
|
@ -32,6 +35,8 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
expanded: false,
|
expanded: false,
|
||||||
|
selectedItem: 0,
|
||||||
|
tooltipShown: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +53,9 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
||||||
<OptionsContext.Consumer>
|
<OptionsContext.Consumer>
|
||||||
{options => (
|
{options => (
|
||||||
<OperationEndpointWrap>
|
<OperationEndpointWrap>
|
||||||
|
<div className={this.state.tooltipShown === true ? 'showToolTip' : 'hideToolTip'}>
|
||||||
|
Copied
|
||||||
|
</div>
|
||||||
<EndpointInfo onClick={this.toggle} expanded={expanded} inverted={inverted}>
|
<EndpointInfo onClick={this.toggle} expanded={expanded} inverted={inverted}>
|
||||||
<HttpVerb type={operation.httpVerb}> {operation.httpVerb}</HttpVerb>{' '}
|
<HttpVerb type={operation.httpVerb}> {operation.httpVerb}</HttpVerb>{' '}
|
||||||
<ServerRelativeURL>{operation.path}</ServerRelativeURL>
|
<ServerRelativeURL>{operation.path}</ServerRelativeURL>
|
||||||
|
@ -60,14 +68,14 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
||||||
/>
|
/>
|
||||||
</EndpointInfo>
|
</EndpointInfo>
|
||||||
<ServersOverlay expanded={expanded}>
|
<ServersOverlay expanded={expanded}>
|
||||||
{operation.servers.map(server => {
|
{operation.servers.map((server, index) => {
|
||||||
const normalizedUrl = options.expandDefaultServerVariables
|
const normalizedUrl = options.expandDefaultServerVariables
|
||||||
? expandDefaultServerVariables(server.url, server.variables)
|
? expandDefaultServerVariables(server.url, server.variables)
|
||||||
: server.url;
|
: server.url;
|
||||||
return (
|
return (
|
||||||
<ServerItem key={normalizedUrl}>
|
<ServerItem className={this.state.selectedItem === index ? 'selected' : ''} key={normalizedUrl}>
|
||||||
<Markdown source={server.description || ''} compact={true} />
|
<Markdown onSelectUrl={this.handleUrl.bind(this, index)} source={server.description || ''} compact={true} />
|
||||||
<SelectOnClick>
|
<SelectOnClick onSelectUrl={this.handleUrl.bind(this, index)}>
|
||||||
<ServerUrl>
|
<ServerUrl>
|
||||||
<span>
|
<span>
|
||||||
{hideHostname || options.hideHostname
|
{hideHostname || options.hideHostname
|
||||||
|
@ -86,4 +94,19 @@ export class Endpoint extends React.Component<EndpointProps, EndpointState> {
|
||||||
</OptionsContext.Consumer>
|
</OptionsContext.Consumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleUrl(url: number) {
|
||||||
|
this.props.handleUrl(url);
|
||||||
|
this.setState({
|
||||||
|
selectedItem: url,
|
||||||
|
expanded: false,
|
||||||
|
tooltipShown: true,
|
||||||
|
});
|
||||||
|
ClipboardService.copyCustom(this.props.operation.servers[url].url + this.props.operation.path);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setState({
|
||||||
|
tooltipShown: false,
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,26 @@ export const OperationEndpointWrap = styled.div`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
.showToolTip {
|
||||||
|
visibility: initial;
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
padding: 3px;
|
||||||
|
position: initial;
|
||||||
|
width: 53px;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
};
|
||||||
|
.hideToolTip {
|
||||||
|
visibility:hidden;
|
||||||
|
padding: 3px;
|
||||||
|
position: initial;
|
||||||
|
width: 53px;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ServerRelativeURL = styled.span`
|
export const ServerRelativeURL = styled.span`
|
||||||
|
@ -66,16 +86,28 @@ export const ServersOverlay = styled.div<{ expanded: boolean }>`
|
||||||
|
|
||||||
export const ServerItem = styled.div`
|
export const ServerItem = styled.div`
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
background-color: #002c2d;
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
&.selected {
|
||||||
|
background-color: #3c7173;
|
||||||
|
}
|
||||||
|
div:first-child {
|
||||||
|
width: 20%;
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ServerUrl = styled.div`
|
export const ServerUrl = styled.div`
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 5px;
|
user-select: none;
|
||||||
border: 1px solid #ccc;
|
padding: 5px !important;
|
||||||
background: #fff;
|
background-color: #ffffff33;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
color: ${props => props.theme.colors.primary.main};
|
width: 100% !important;
|
||||||
|
color: #00ff1c;
|
||||||
> span {
|
> span {
|
||||||
color: ${props => props.theme.colors.text.primary};
|
color: white;
|
||||||
};
|
};
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { MarkdownRenderer } from '../../services';
|
import { MarkdownRenderer } from '../../services';
|
||||||
|
import styled from '../../styled-components';
|
||||||
import { SanitizedMarkdownHTML } from './SanitizedMdBlock';
|
import { SanitizedMarkdownHTML } from './SanitizedMdBlock';
|
||||||
|
|
||||||
export interface StylingMarkdownProps {
|
export interface StylingMarkdownProps {
|
||||||
|
@ -17,19 +18,31 @@ export type MarkdownProps = BaseMarkdownProps &
|
||||||
StylingMarkdownProps & {
|
StylingMarkdownProps & {
|
||||||
source: string;
|
source: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
onSelectUrl?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Markdown extends React.Component<MarkdownProps> {
|
export class Markdown extends React.Component<MarkdownProps> {
|
||||||
|
handleClick = () => {
|
||||||
|
this.props.onSelectUrl();
|
||||||
|
};
|
||||||
render() {
|
render() {
|
||||||
const { source, inline, compact, className } = this.props;
|
const { source, inline, compact, className } = this.props;
|
||||||
const renderer = new MarkdownRenderer();
|
const renderer = new MarkdownRenderer();
|
||||||
return (
|
return (
|
||||||
<SanitizedMarkdownHTML
|
<MarkWrapper onClick={this.handleClick}>
|
||||||
html={renderer.renderMd(source)}
|
<SanitizedMarkdownHTML
|
||||||
inline={inline}
|
html={renderer.renderMd(source)}
|
||||||
compact={compact}
|
inline={inline}
|
||||||
className={className}
|
compact={compact}
|
||||||
/>
|
className={className}
|
||||||
|
/>
|
||||||
|
</MarkWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const MarkWrapper = styled.div`
|
||||||
|
div {
|
||||||
|
width: 100% !important;
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -38,6 +38,7 @@ export interface OperationProps {
|
||||||
|
|
||||||
export interface OperationState {
|
export interface OperationState {
|
||||||
executeMode: boolean;
|
executeMode: boolean;
|
||||||
|
urlIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
|
@ -47,6 +48,7 @@ export class Operation extends React.Component<OperationProps, OperationState> {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
executeMode: false,
|
executeMode: false,
|
||||||
|
urlIndex: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +81,7 @@ export class Operation extends React.Component<OperationProps, OperationState> {
|
||||||
label="Try it out!"
|
label="Try it out!"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
|
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} handleUrl={this.onUrlChanged}/>}
|
||||||
{hasDescription && (
|
{hasDescription && (
|
||||||
<Description>
|
<Description>
|
||||||
{description !== undefined && <Markdown source={description} />}
|
{description !== undefined && <Markdown source={description} />}
|
||||||
|
@ -92,10 +94,10 @@ export class Operation extends React.Component<OperationProps, OperationState> {
|
||||||
<ResponsesList responses={operation.responses} />
|
<ResponsesList responses={operation.responses} />
|
||||||
</MiddlePanel>
|
</MiddlePanel>
|
||||||
<DarkRightPanel>
|
<DarkRightPanel>
|
||||||
{!options.pathInMiddlePanel && <Endpoint operation={operation} />}
|
{!options.pathInMiddlePanel && <Endpoint operation={operation} handleUrl={this.onUrlChanged}/>}
|
||||||
{executeMode &&
|
{executeMode &&
|
||||||
<div>
|
<div>
|
||||||
<ConsoleViewer operation={operation} additionalHeaders={options.additionalHeaders} queryParamPrefix={options.queryParamPrefix} queryParamSuffix={options.queryParamSuffix} />
|
<ConsoleViewer urlIndex={this.state.urlIndex} operation={operation} additionalHeaders={options.additionalHeaders} queryParamPrefix={options.queryParamPrefix} queryParamSuffix={options.queryParamSuffix} />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{!executeMode &&
|
{!executeMode &&
|
||||||
|
@ -110,4 +112,9 @@ export class Operation extends React.Component<OperationProps, OperationState> {
|
||||||
</OptionsContext.Consumer>
|
</OptionsContext.Consumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
onUrlChanged = (index= 0) => {
|
||||||
|
this.setState({
|
||||||
|
urlIndex: index,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,28 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { ClipboardService } from '../../services';
|
import { ClipboardService } from '../../services';
|
||||||
|
import styled from '../../styled-components';
|
||||||
|
|
||||||
export class SelectOnClick extends React.PureComponent {
|
interface SelectOnClickProps {
|
||||||
|
onSelectUrl: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SelectOnClick extends React.PureComponent<SelectOnClickProps> {
|
||||||
private child: HTMLDivElement | null;
|
private child: HTMLDivElement | null;
|
||||||
handleClick = () => {
|
handleClick = () => {
|
||||||
ClipboardService.selectElement(this.child);
|
ClipboardService.selectElement(this.child);
|
||||||
|
this.props.onSelectUrl();
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { children } = this.props;
|
const { children } = this.props;
|
||||||
return (
|
return (
|
||||||
<div ref={el => (this.child = el)} onClick={this.handleClick}>
|
<SelectArea ref={el => (this.child = el)} onClick={this.handleClick.bind(this, children)}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</SelectArea>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const SelectArea = styled.div`
|
||||||
|
width: 80%;
|
||||||
|
`;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user