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:
Arian Rahimi 2020-02-01 11:12:05 +03:30
commit 89e8dc8dcb
7 changed files with 224 additions and 24 deletions

View File

@ -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>
); );

View 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;
}
`;

View File

@ -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);
}
} }

View File

@ -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;
}; };
`; `;

View File

@ -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;
}
`;

View File

@ -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,
});
}
} }

View File

@ -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%;
`;