From 95376a17b9a5052b45c09a5ac656d65a24d6622e Mon Sep 17 00:00:00 2001 From: Chaiwat Ekkaewnumchai Date: Tue, 17 Mar 2020 10:05:27 -0700 Subject: [PATCH] (Server) Add MockResponseDetail and Package Used Inside Summary: - Add MockResponseDetail component to show and edit mocked route - Add `immer` package which will be used for internal state handler Change from PR - Change to functional component Note: - This is a part of this PR: https://github.com/facebook/flipper/pull/488 Reviewed By: mweststrate Differential Revision: D20440149 fbshipit-source-id: d7c35600b9a22cb62c2bdae7e19abe5c767e3670 --- .../plugins/network/MockResponseDetails.tsx | 334 ++++++++++++++++++ desktop/plugins/network/package.json | 10 +- 2 files changed, 340 insertions(+), 4 deletions(-) create mode 100644 desktop/plugins/network/MockResponseDetails.tsx diff --git a/desktop/plugins/network/MockResponseDetails.tsx b/desktop/plugins/network/MockResponseDetails.tsx new file mode 100644 index 000000000..5caccb5cc --- /dev/null +++ b/desktop/plugins/network/MockResponseDetails.tsx @@ -0,0 +1,334 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import { + FlexRow, + FlexColumn, + FlexBox, + Input, + Text, + Tabs, + Tab, + Glyph, + ManagedTable, + Select, + styled, + colors, + produce, +} from 'flipper'; +import React, {useContext, useState} from 'react'; +import {NetworkRouteContext, NetworkRouteManager} from './index'; +import {RequestId, Route} from './types'; + +type Props = { + id: RequestId; + route: Route; + isDuplicated: boolean; +}; + +const StyledSelectContainer = styled(FlexRow)({ + paddingLeft: 6, + paddingTop: 2, + paddingBottom: 24, + height: '100%', + flexGrow: 1, +}); + +const StyledSelect = styled(Select)({ + height: '100%', + maxWidth: 200, +}); + +const StyledText = styled(Text)({ + marginLeft: 6, + marginTop: 8, +}); + +const textAreaStyle: React.CSSProperties = { + width: '100%', + marginTop: 8, + height: 300, + fontSize: 15, + color: '#333', + padding: 10, + resize: 'none', + fontFamily: + 'source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace', + display: 'inline-block', + lineHeight: 1.5, + border: '1px solid #dcdee2', + borderRadius: 4, + backgroundColor: '#fff', + cursor: 'text', + WebkitTapHighlightColor: 'transparent', + whiteSpace: 'pre-wrap', + overflowWrap: 'break-word', +}; + +const StyledInput = styled(Input)({ + width: '100%', + height: 20, + marginLeft: 8, + flexGrow: 5, +}); + +const HeaderInput = styled(Input)({ + width: '100%', + height: 20, + marginTop: 6, + marginBottom: 6, +}); + +const HeaderGlyph = styled(Glyph)({ + marginTop: 6, + marginBottom: 6, +}); + +const Container = styled(FlexColumn)({ + flexWrap: 'nowrap', + alignItems: 'flex-start', + alignContent: 'flex-start', + flexGrow: 1, + overflow: 'hidden', +}); + +const Warning = styled(FlexRow)({ + marginTop: 8, +}); + +const AddHeaderButton = styled(FlexBox)({ + color: colors.blackAlpha50, + marginTop: 8, + alignItems: 'center', + padding: 10, + flexShrink: 0, + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', +}); + +const HeadersColumnSizes = { + name: '40%', + value: '40%', + close: '10%', + warning: 'flex', +}; + +const HeadersColumns = { + name: { + value: 'Name', + resizable: false, + }, + value: { + value: 'Value', + resizable: false, + }, + close: { + value: '', + resizable: false, + }, + warning: { + value: '', + resizable: false, + }, +}; + +function _buildMockResponseHeaderRows( + routeId: string, + route: Route, + selectedHeaderId: string | null, + networkRouteManager: NetworkRouteManager, +) { + const selectedHighlight = {backgroundColor: colors.highlight}; + + return Object.entries(route.responseHeaders).map(([id, header]) => { + const selected = selectedHeaderId === id; + return { + columns: { + name: { + value: ( + { + if (!selected) { + return; + } + const newHeaders = produce( + route.responseHeaders, + draftHeaders => { + draftHeaders[id].key = event.target.value; + }, + ); + networkRouteManager.modifyRoute(routeId, { + responseHeaders: newHeaders, + }); + }} + /> + ), + }, + value: { + value: ( + { + if (!selected) { + return; + } + const newHeaders = produce( + route.responseHeaders, + draftHeaders => { + draftHeaders[id].value = event.target.value; + }, + ); + networkRouteManager.modifyRoute(routeId, { + responseHeaders: newHeaders, + }); + }} + /> + ), + }, + close: { + value: ( + { + const newHeaders = produce( + route.responseHeaders, + draftHeaders => { + delete draftHeaders[id]; + }, + ); + networkRouteManager.modifyRoute(routeId, { + responseHeaders: newHeaders, + }); + }}> + + + ), + }, + }, + key: id, + }; + }); +} + +export function MockResponseDetails({id, route, isDuplicated}: Props) { + const networkRouteManager = useContext(NetworkRouteContext); + const [activeTab, setActiveTab] = useState('data'); + const [selectedHeaderIds, setSelectedHeaderIds] = useState>( + [], + ); + const [nextHeaderId, setNextHeaderId] = useState(0); + + const {requestUrl, requestMethod, responseData} = route; + return ( + + + + + networkRouteManager.modifyRoute(id, {requestMethod: text}) + } + /> + + + networkRouteManager.modifyRoute(id, { + requestUrl: event.target.value, + }) + } + /> + + {isDuplicated && ( + + + + Route is duplicated (Same URL and Method) + + + )} + + { + if (newActiveTab != null) { + setActiveTab(newActiveTab); + } + }}> + +