Skip to content

Commit 6acfe42

Browse files
committed
Enable Send card expansion, and refactor expansion animation globally
1 parent 86653d8 commit 6acfe42

21 files changed

+259
-199
lines changed

src/components/common/card.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,13 @@ export const BigCard = styled(MediumCard)`
124124
}
125125
`;
126126

127+
// Starting is a very brief temporary state, used to show a card as expanded but
128+
// apply a brief animation, when expansion is first triggered
129+
export type ExpandState = boolean | 'starting';
130+
127131
export interface CollapsibleCardProps {
128132
collapsed: boolean;
129-
expanded?: boolean;
133+
expanded?: ExpandState;
130134

131135
// The highlighted content direction - shows a border on the
132136
// left or right of the whole card to indicate up/downstream
@@ -140,6 +144,12 @@ export interface CollapsibleCardProps {
140144
onCollapseToggled?: () => void;
141145
}
142146

147+
// A convenient type for always-expandable cards, where relevant properties are strictly required:
148+
export interface ExpandableCardProps extends CollapsibleCardProps {
149+
expanded: ExpandState;
150+
onExpandToggled: () => void;
151+
}
152+
143153
@observer
144154
export class CollapsibleCard extends React.Component<
145155
CollapsibleCardProps & React.HTMLAttributes<HTMLDivElement>
@@ -245,7 +255,7 @@ const cardDirectionCss = (direction?: string) =>
245255

246256
const CollapsibleCardContainer = styled(MediumCard)<{
247257
collapsed: boolean;
248-
expanded: boolean;
258+
expanded: ExpandState;
249259
direction?: 'left' | 'right';
250260
}>`
251261
display: flex;
@@ -260,13 +270,24 @@ const CollapsibleCardContainer = styled(MediumCard)<{
260270
`}
261271
262272
${p => p.expanded && css`
273+
/* Override the Send container setting this to 'none', which hides non-expanded parts: */
274+
display: flex !important;
275+
263276
height: 100%;
264277
width: 100%;
265278
border-radius: 0;
266279
margin: 0;
267280
268281
flex-shrink: 1;
269282
min-height: 0;
283+
284+
${p.expanded === 'starting'
285+
? `
286+
padding-top: 40px;
287+
padding-bottom: 40px;
288+
`
289+
: 'transition: padding 0.1s;'
290+
}
270291
`}
271292
272293
&:focus {

src/components/common/expand-shrink-button.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import * as React from 'react';
22

33
import { IconButton } from './icon-button';
4+
import { ExpandState } from './card';
45

5-
export const ExpandShrinkButton = (p: { expanded: boolean, onClick: () => void }) =>
6+
export const ExpandShrinkButton = (p: {
7+
expanded: ExpandState | undefined,
8+
onClick: () => void
9+
}) =>
610
<IconButton
711
icon={[
812
'fas',

src/components/editor/body-card-components.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
} from '../../model/events/content-types';
1717
import { getReadableSize } from '../../model/events/bodies';
1818

19-
import { CollapsibleCardHeading } from '../common/card';
19+
import { CollapsibleCardHeading, ExpandState } from '../common/card';
2020
import { CollapsingButtons } from '../common/collapsing-buttons';
2121
import { Pill, PillSelector } from '../common/pill';
2222
import { ExpandShrinkButton } from '../common/expand-shrink-button';
@@ -72,9 +72,9 @@ export const ReadonlyBodyCardHeader = (props: {
7272
downloadFilename?: string,
7373

7474
title: string,
75-
expanded: boolean,
75+
expanded: ExpandState,
7676
onExpandToggled: () => void,
77-
onCollapseToggled: () => void,
77+
onCollapseToggled?: () => void,
7878

7979
selectedContentType: ViewableContentType,
8080
contentTypeOptions: readonly ViewableContentType[],
@@ -125,9 +125,9 @@ export const EditableBodyCardHeader = (props: {
125125
onBodyFormatted: (bodyString: string) => void,
126126

127127
title: string,
128-
expanded: boolean,
128+
expanded: ExpandState,
129129
onExpandToggled: () => void,
130-
onCollapseToggled: () => void,
130+
onCollapseToggled?: () => void,
131131

132132
selectedContentType: EditableContentType,
133133
contentTypeOptions: readonly EditableContentType[],

src/components/send/request-pane.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as portals from 'react-reverse-portal';
66
import { Method } from 'mockttp';
77

88
import { RawHeaders } from '../../types';
9-
import { styled } from '../../styles';
9+
import { css, styled } from '../../styles';
1010
import { Icon } from '../../icons';
1111

1212
import { RulesStore } from '../../model/rules/rules-store';
@@ -18,10 +18,19 @@ import { ContainerSizedEditor } from '../editor/base-editor';
1818
import { SendRequestHeadersCard } from './send-request-headers-card';
1919
import { SendRequestBodyCard } from './send-request-body-card';
2020

21-
const RequestPaneContainer = styled.section`
21+
const RequestPaneContainer = styled.section<{
22+
hasExpandedChild: boolean
23+
}>`
2224
display: flex;
2325
flex-direction: column;
2426
height: 100%;
27+
28+
${p => p.hasExpandedChild && css`
29+
> * {
30+
/* CollapsibleCard applies its own display property to override this for the expanded card */
31+
display: none;
32+
}
33+
`}
2534
`;
2635

2736
// Layout here is tricky. Current setup seems to work (flex hrink everywhere, grow bodies,
@@ -68,9 +77,9 @@ export class RequestPane extends React.Component<{
6877
}
6978

7079
render() {
71-
const { requestInput, editorNode } = this.props;
80+
const { requestInput, editorNode, uiStore } = this.props;
7281

73-
return <RequestPaneContainer>
82+
return <RequestPaneContainer hasExpandedChild={!!uiStore?.expandedSendRequestCard}>
7483
<MethodSelect value={requestInput.method} onChange={this.updateMethod}>
7584
{ validMethods.map((methodOption) =>
7685
<option

src/components/send/response-pane.tsx

Lines changed: 62 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import * as React from "react";
22
import { inject, observer } from "mobx-react";
33
import * as portals from 'react-reverse-portal';
44

5-
import { styled } from '../../styles';
5+
import { css, styled } from '../../styles';
66
import { HttpExchange } from "../../types";
77

88
import { UiStore } from '../../model/ui/ui-store';
99
import { AccountStore } from '../../model/account/account-store';
10+
import { SuccessfulExchange } from "../../model/http/exchange";
1011

1112
import { ContainerSizedEditor } from '../editor/base-editor';
1213
import { LoadingCard } from '../common/loading-card';
@@ -16,10 +17,19 @@ import { ResponseStatusSection } from './sent-response-status';
1617
import { SentResponseHeaderSection } from './sent-response-headers';
1718
import { SentResponseBodyCard } from './sent-response-body';
1819

19-
const ResponsePaneContainer = styled.section`
20+
const ResponsePaneContainer = styled.section<{
21+
hasExpandedChild: boolean
22+
}>`
2023
display: flex;
2124
flex-direction: column;
2225
height: 100%;
26+
27+
${p => p.hasExpandedChild && css`
28+
> * {
29+
/* CollapsibleCard applies its own display property to override this for the expanded card */
30+
display: none;
31+
}
32+
`}
2333
`;
2434

2535
@inject('uiStore')
@@ -38,45 +48,58 @@ export class ResponsePane extends React.Component<{
3848
}
3949

4050
render() {
41-
const { exchange, uiStore, editorNode } = this.props;
51+
const { exchange, uiStore } = this.props;
4252
if (!exchange) return null;
4353

44-
if (exchange.isSuccessfulExchange()) {
45-
const response = exchange.response;
46-
return <ResponsePaneContainer>
47-
<ResponseStatusSection
48-
exchange={exchange}
49-
theme={uiStore!.theme}
50-
/>
51-
<SentResponseHeaderSection
52-
requestUrl={exchange.request.parsedUrl}
53-
headers={response.rawHeaders}
54-
{...this.cardProps.responseHeaders}
55-
/>
56-
<SentResponseBodyCard
57-
{...this.cardProps.responseBody}
58-
isPaidUser={this.props.accountStore!.isPaidUser}
59-
url={exchange.request.url}
60-
message={response}
61-
editorNode={editorNode}
62-
/>
63-
</ResponsePaneContainer>;
64-
} else if (exchange.isCompletedExchange()) {
65-
return <ResponsePaneContainer>
66-
<HttpAbortedResponseCard
67-
cardProps={this.cardProps.responseHeaders}
68-
exchange={exchange}
69-
/>
70-
</ResponsePaneContainer>
71-
} else {
72-
return <ResponsePaneContainer>
73-
<LoadingCard {...this.cardProps.responseHeaders}>
74-
<header>
75-
<h1>Response...</h1>
76-
</header>
77-
</LoadingCard>
78-
</ResponsePaneContainer>;
79-
}
54+
return <ResponsePaneContainer hasExpandedChild={!!uiStore?.expandedSentResponseCard}>
55+
{
56+
exchange.isSuccessfulExchange()
57+
? this.renderSuccessfulResponse(exchange)
58+
: exchange.isCompletedExchange()
59+
? this.renderAbortedResponse(exchange)
60+
: this.renderInProgressResponse()
61+
}
62+
</ResponsePaneContainer>;
63+
}
64+
65+
renderSuccessfulResponse(exchange: SuccessfulExchange) {
66+
const { uiStore, editorNode } = this.props;
67+
const response = exchange.response;
68+
69+
return <>
70+
<ResponseStatusSection
71+
exchange={exchange}
72+
theme={uiStore!.theme}
73+
/>
74+
<SentResponseHeaderSection
75+
{...this.cardProps.responseHeaders}
76+
requestUrl={exchange.request.parsedUrl}
77+
headers={response.rawHeaders}
78+
/>
79+
<SentResponseBodyCard
80+
{...this.cardProps.responseBody}
81+
isPaidUser={this.props.accountStore!.isPaidUser}
82+
url={exchange.request.url}
83+
message={response}
84+
editorNode={editorNode}
85+
/>
86+
</>;
87+
88+
}
89+
90+
renderAbortedResponse(exchange: HttpExchange) {
91+
return <HttpAbortedResponseCard
92+
cardProps={this.cardProps.responseHeaders}
93+
exchange={exchange}
94+
/>;
95+
}
96+
97+
renderInProgressResponse() {
98+
return <LoadingCard {...this.cardProps.responseHeaders}>
99+
<header>
100+
<h1>Response...</h1>
101+
</header>
102+
</LoadingCard>;
80103
}
81104

82105
}

src/components/send/send-request-body-card.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,21 @@ import { action, computed, observable } from 'mobx';
33
import { observer } from 'mobx-react';
44
import * as portals from 'react-reverse-portal';
55

6-
import { styled } from '../../styles';
7-
86
import { bufferToString, isProbablyUtf8, stringToBuffer } from '../../util';
97

108
import { EditableContentType, EditableContentTypes } from '../../model/events/content-types';
119

12-
import { CollapsibleCardProps } from '../common/card';
10+
import { ExpandableCardProps } from '../common/card';
1311
import { SendBodyCardSection } from './send-card-section';
1412
import { ContainerSizedEditor } from '../editor/base-editor';
1513
import {
1614
EditableBodyCardHeader,
1715
ContainerSizedEditorCardContent
1816
} from '../editor/body-card-components';
1917

20-
export interface SendRequestBodyProps extends CollapsibleCardProps {
18+
export interface SendRequestBodyProps extends ExpandableCardProps {
2119
body: Buffer;
2220
onBodyUpdated: (body: Buffer) => void;
23-
expanded: boolean;
24-
onCollapseToggled: () => void;
25-
onExpandToggled: () => void;
26-
2721
editorNode: portals.HtmlPortalNode<typeof ContainerSizedEditor>;
2822
}
2923

src/components/send/send-request-headers-card.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ import { RawHeaders } from '../../types';
66

77
import {
88
CollapsibleCardHeading,
9-
CollapsibleCardProps
9+
ExpandableCardProps
1010
} from '../common/card';
1111
import {
1212
SendCardSection
1313
} from './send-card-section';
1414
import { EditableRawHeaders } from '../common/editable-headers';
15+
import { ExpandShrinkButton } from '../common/expand-shrink-button';
16+
import { CollapsingButtons } from '../common/collapsing-buttons';
1517

16-
export interface SendRequestHeadersProps extends CollapsibleCardProps {
18+
export interface SendRequestHeadersProps extends ExpandableCardProps {
1719
headers: RawHeaders;
1820
updateHeaders: (headers: RawHeaders) => void;
1921
}
@@ -28,17 +30,30 @@ const HeaderFieldsContainer = styled.div`
2830
padding: 0 20px 20px 20px;
2931
`;
3032

31-
export const SendRequestHeadersCard = observer((props: SendRequestHeadersProps) => {
32-
return <SendCardSection {...props} headerAlignment='left'>
33+
export const SendRequestHeadersCard = observer(({
34+
headers,
35+
updateHeaders,
36+
...cardProps
37+
}: SendRequestHeadersProps) => {
38+
return <SendCardSection
39+
{...cardProps}
40+
headerAlignment='left'
41+
>
3342
<header>
34-
<CollapsibleCardHeading onCollapseToggled={props.onCollapseToggled}>
43+
<CollapsingButtons>
44+
<ExpandShrinkButton
45+
expanded={cardProps.expanded}
46+
onClick={cardProps.onExpandToggled}
47+
/>
48+
</CollapsingButtons>
49+
<CollapsibleCardHeading onCollapseToggled={cardProps.onCollapseToggled}>
3550
Request Headers
3651
</CollapsibleCardHeading>
3752
</header>
3853
<HeaderFieldsContainer>
3954
<EditableRawHeaders
40-
headers={props.headers}
41-
onChange={props.updateHeaders}
55+
headers={headers}
56+
onChange={updateHeaders}
4257
/>
4358
</HeaderFieldsContainer>
4459
</SendCardSection>;

0 commit comments

Comments
 (0)