mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-08 14:14:56 +03:00
Match rendering of callbacks to style of operation responses
This commit is contained in:
parent
01809ca644
commit
4bd2e70940
87
src/components/CallbackSamples/CallbackSamples.tsx
Normal file
87
src/components/CallbackSamples/CallbackSamples.tsx
Normal file
|
@ -0,0 +1,87 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
import { isPayloadSample, RedocNormalizedOptions } from '../../services';
|
||||
import { PayloadSamples } from '../PayloadSamples/PayloadSamples';
|
||||
import { SourceCodeWithCopy } from '../SourceCode/SourceCode';
|
||||
|
||||
import { RightPanelHeader, Tab, TabList, TabPanel, Tabs } from '../../common-elements';
|
||||
import { OptionsContext } from '../OptionsProvider';
|
||||
import { CallbackModel } from '../../services/models';
|
||||
import { Endpoint } from '../Endpoint/Endpoint';
|
||||
|
||||
export interface CallbackSamplesProps {
|
||||
callbacks: CallbackModel[];
|
||||
}
|
||||
|
||||
@observer
|
||||
export class CallbackSamples extends React.Component<CallbackSamplesProps> {
|
||||
static contextType = OptionsContext;
|
||||
context: RedocNormalizedOptions;
|
||||
|
||||
render() {
|
||||
const { callbacks } = this.props;
|
||||
|
||||
// Sums number of code samples per operation per callback
|
||||
const numSamples = callbacks.reduce(
|
||||
(callbackSum, callback) =>
|
||||
callbackSum +
|
||||
callback.operations.reduce(
|
||||
(sampleSum, operation) => sampleSum + operation.codeSamples.length,
|
||||
0,
|
||||
),
|
||||
0,
|
||||
);
|
||||
|
||||
const hasSamples = numSamples > 0;
|
||||
const hideTabList = numSamples === 1 ? this.context.hideSingleRequestSampleTab : false;
|
||||
|
||||
const renderTabs = () => {
|
||||
return callbacks.map(callback => {
|
||||
return callback.operations.map(operation => {
|
||||
return operation.codeSamples.map(sample => {
|
||||
return (
|
||||
<Tab key={operation.id + '_' + operation.name}>
|
||||
{operation.name} {sample.label !== undefined ? sample.label : sample.lang}
|
||||
</Tab>
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const renderTabPanels = () => {
|
||||
return callbacks.map(callback => {
|
||||
return callback.operations.map(operation => {
|
||||
return operation.codeSamples.map(sample => {
|
||||
return (
|
||||
<TabPanel key={sample.lang + '_' + (sample.label || '')}>
|
||||
{isPayloadSample(sample) ? (
|
||||
<div>
|
||||
<Endpoint operation={operation} inverted={false} />
|
||||
<PayloadSamples content={sample.requestBodyContent} />
|
||||
</div>
|
||||
) : (
|
||||
<SourceCodeWithCopy lang={sample.lang} source={sample.source} />
|
||||
)}
|
||||
</TabPanel>
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
(hasSamples && (
|
||||
<div>
|
||||
<RightPanelHeader> Callback samples </RightPanelHeader>
|
||||
|
||||
<Tabs defaultIndex={0}>
|
||||
<TabList hidden={hideTabList}>{renderTabs()}</TabList>
|
||||
{renderTabPanels()}
|
||||
</Tabs>
|
||||
</div>
|
||||
)) ||
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,23 +1,29 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
import { CallbackModel } from '../../services/models';
|
||||
import { OperationModel } from '../../services/models';
|
||||
import { CallbackDetails } from './CallbackDetails';
|
||||
import { CallbackDetailsWrap, StyledCallbackTitle } from '../Callbacks/styled.elements';
|
||||
|
||||
@observer
|
||||
export class CallbackView extends React.Component<{ callback: CallbackModel }> {
|
||||
export class CallbackView extends React.Component<{ callbackOperation: OperationModel }> {
|
||||
toggle = () => {
|
||||
this.props.callback.toggle();
|
||||
this.props.callbackOperation.toggle();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { name, expanded } = this.props.callback;
|
||||
const { name, description, expanded } = this.props.callbackOperation;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StyledCallbackTitle onClick={this.toggle} name={name} opened={expanded} />
|
||||
<StyledCallbackTitle
|
||||
onClick={this.toggle}
|
||||
name={name}
|
||||
description={description}
|
||||
opened={expanded}
|
||||
/>
|
||||
{expanded && (
|
||||
<CallbackDetailsWrap>
|
||||
<span>{name}</span>
|
||||
<CallbackDetails callbackOperation={this.props.callbackOperation} />
|
||||
</CallbackDetailsWrap>
|
||||
)}
|
||||
</div>
|
||||
|
|
9
src/components/Callbacks/CallbackDetails.tsx
Normal file
9
src/components/Callbacks/CallbackDetails.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import * as React from 'react';
|
||||
import { OperationModel } from '../../services/models';
|
||||
import { OperationItem } from '../ContentItems/ContentItems';
|
||||
|
||||
export class CallbackDetails extends React.PureComponent<{ callbackOperation: OperationModel }> {
|
||||
render() {
|
||||
return <OperationItem item={this.props.callbackOperation} />;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { ShelfIcon } from '../../common-elements';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
|
||||
export interface CallbackTitleProps {
|
||||
name: string;
|
||||
description?: string;
|
||||
opened?: boolean;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
|
@ -11,11 +13,12 @@ export interface CallbackTitleProps {
|
|||
|
||||
export class CallbackTitle extends React.PureComponent<CallbackTitleProps> {
|
||||
render() {
|
||||
const { name, opened, className, onClick } = this.props;
|
||||
const { name, description, opened, className, onClick } = this.props;
|
||||
return (
|
||||
<div className={className} onClick={onClick || undefined}>
|
||||
<ShelfIcon size={'1.5em'} direction={opened ? 'up' : 'down'} float={'left'} />
|
||||
<ShelfIcon size={'1.5em'} direction={opened ? 'down' : 'right'} float={'left'} />
|
||||
<strong>{name} </strong>
|
||||
{description && <Markdown compact={true} inline={true} source={description} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@ export class CallbacksList extends React.PureComponent<CallbacksListProps> {
|
|||
<div>
|
||||
<CallbacksHeader> Callbacks </CallbacksHeader>
|
||||
{callbacks.map(callback => {
|
||||
return <CallbackView key={callback.name} callback={callback} />;
|
||||
return callback.operations.map(operation => {
|
||||
return <CallbackView key={callback.name} callbackOperation={operation} />;
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -8,15 +8,6 @@ import { Operation } from '..';
|
|||
import { H1, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
||||
import { ContentItemModel } from '../../services';
|
||||
import { GroupModel, OperationModel } from '../../services/models';
|
||||
import styled from '../../styled-components';
|
||||
|
||||
const CallbacksHeader = styled.h3`
|
||||
font-size: 18px;
|
||||
padding: 0 40px;
|
||||
margin: 3em 0 1.1em;
|
||||
color: #253137;
|
||||
font-weight: normal;
|
||||
`;
|
||||
|
||||
@observer
|
||||
export class ContentItems extends React.Component<{
|
||||
|
@ -28,18 +19,6 @@ export class ContentItems extends React.Component<{
|
|||
return null;
|
||||
}
|
||||
return items.map(item => {
|
||||
if (item.type === 'operation' && item.callbacks.length > 0) {
|
||||
return (
|
||||
<React.Fragment key={item.id}>
|
||||
<ContentItem item={item} />
|
||||
<CallbacksHeader>Callbacks</CallbacksHeader>
|
||||
{item.callbacks.map((callbackIndex, idx) => {
|
||||
return <ContentItems key={idx} items={callbackIndex.operations} />;
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return <ContentItem key={item.id} item={item} />;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,11 +15,18 @@ import { Parameters } from '../Parameters/Parameters';
|
|||
import { RequestSamples } from '../RequestSamples/RequestSamples';
|
||||
import { ResponsesList } from '../Responses/ResponsesList';
|
||||
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
||||
import { CallbacksList } from '../Callbacks';
|
||||
import { CallbackSamples } from '../CallbackSamples/CallbackSamples';
|
||||
|
||||
import { OperationModel as OperationType } from '../../services/models';
|
||||
import styled from '../../styled-components';
|
||||
import { Extensions } from '../Fields/Extensions';
|
||||
|
||||
const CallbackMiddlePanel = styled(MiddlePanel)`
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
`;
|
||||
|
||||
const OperationRow = styled(Row)`
|
||||
backface-visibility: hidden;
|
||||
contain: content;
|
||||
|
@ -42,18 +49,23 @@ export class Operation extends React.Component<OperationProps> {
|
|||
|
||||
const { name: summary, description, deprecated, externalDocs } = operation;
|
||||
const hasDescription = !!(description || externalDocs);
|
||||
const AdaptiveMiddlePanel = operation.isCallback ? CallbackMiddlePanel : MiddlePanel;
|
||||
|
||||
return (
|
||||
<OptionsContext.Consumer>
|
||||
{options => (
|
||||
<OperationRow>
|
||||
<MiddlePanel>
|
||||
<AdaptiveMiddlePanel>
|
||||
{!operation.isCallback && (
|
||||
<H2>
|
||||
<ShareLink to={operation.id} />
|
||||
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
|
||||
</H2>
|
||||
{options.pathInMiddlePanel && <Endpoint operation={operation} inverted={true} />}
|
||||
{hasDescription && (
|
||||
)}
|
||||
{!operation.isCallback && options.pathInMiddlePanel && (
|
||||
<Endpoint operation={operation} inverted={true} />
|
||||
)}
|
||||
{!operation.isCallback && hasDescription && (
|
||||
<Description>
|
||||
{description !== undefined && <Markdown source={description} />}
|
||||
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
|
||||
|
@ -63,12 +75,18 @@ export class Operation extends React.Component<OperationProps> {
|
|||
<SecurityRequirements securities={operation.security} />
|
||||
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
||||
<ResponsesList responses={operation.responses} />
|
||||
</MiddlePanel>
|
||||
<CallbacksList callbacks={operation.callbacks} />
|
||||
</AdaptiveMiddlePanel>
|
||||
{!operation.isCallback && (
|
||||
<DarkRightPanel>
|
||||
{!options.pathInMiddlePanel && <Endpoint operation={operation} />}
|
||||
<RequestSamples operation={operation} />
|
||||
<ResponseSamples operation={operation} />
|
||||
{operation.callbacks.length > 0 && (
|
||||
<CallbackSamples callbacks={operation.callbacks} />
|
||||
)}
|
||||
</DarkRightPanel>
|
||||
)}
|
||||
</OperationRow>
|
||||
)}
|
||||
</OptionsContext.Consumer>
|
||||
|
|
|
@ -22,7 +22,7 @@ describe('Components', () => {
|
|||
options,
|
||||
);
|
||||
const callbackViewElement = shallow(
|
||||
<CallbackView key={callback.name} callback={callback} />,
|
||||
<CallbackView key={callback.name} callbackOperation={callback.operations[0]} />,
|
||||
).getElement();
|
||||
expect(callbackViewElement.props).toBeDefined();
|
||||
expect(callbackViewElement.props.children).toBeDefined();
|
||||
|
|
|
@ -172,6 +172,14 @@ export class OperationModel implements IMenuItem {
|
|||
this.active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle expansion in middle panel (for callbacks, which are operations)
|
||||
*/
|
||||
@action
|
||||
toggle() {
|
||||
this.expanded = !this.expanded;
|
||||
}
|
||||
|
||||
expand() {
|
||||
if (this.parent) {
|
||||
this.parent.expand();
|
||||
|
|
Loading…
Reference in New Issue
Block a user