mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-22 07:08:10 +00:00
convert components in common and ContentView folder into typescript, and modified test
This commit is contained in:
parent
7ff97d6e08
commit
6def195743
@ -19,16 +19,12 @@ describe('ViewImage Component', () => {
|
||||
})
|
||||
|
||||
describe('ViewServer Component', () => {
|
||||
let store = TStore(),
|
||||
setContentViewDescFn = jest.fn(),
|
||||
setContentFn = jest.fn()
|
||||
let store = TStore()
|
||||
|
||||
it('should render correctly and connect to state', () => {
|
||||
let provider = renderer.create(
|
||||
<Provider store={store}>
|
||||
<ViewServer
|
||||
setContentViewDescription={setContentViewDescFn}
|
||||
setContent={setContentFn}
|
||||
flow={tflow}
|
||||
message={tflow.response}
|
||||
/>
|
||||
@ -37,38 +33,17 @@ describe('ViewServer Component', () => {
|
||||
expect(tree).toMatchSnapshot()
|
||||
|
||||
let viewServer = renderer.create(
|
||||
<Provider store={store}>
|
||||
<PureViewServer
|
||||
showFullContent={true}
|
||||
maxLines={10}
|
||||
setContentViewDescription={setContentViewDescFn}
|
||||
setContent={setContentViewDescFn}
|
||||
flow={tflow}
|
||||
message={tflow.response}
|
||||
content={JSON.stringify({lines: [['k1', 'v1']]})}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
tree = viewServer.toJSON()
|
||||
expect(tree).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should handle componentWillReceiveProps', () => {
|
||||
// case of fail to parse content
|
||||
let viewServer = TestUtils.renderIntoDocument(
|
||||
<PureViewServer
|
||||
showFullContent={true}
|
||||
maxLines={10}
|
||||
setContentViewDescription={setContentViewDescFn}
|
||||
setContent={setContentViewDescFn}
|
||||
flow={tflow}
|
||||
message={tflow.response}
|
||||
content={JSON.stringify({lines: [['k1', 'v1']]})}
|
||||
/>
|
||||
)
|
||||
viewServer.UNSAFE_componentWillReceiveProps({...viewServer.props, content: '{foo' })
|
||||
let e = ''
|
||||
try {JSON.parse('{foo') } catch(err){ e = err.message}
|
||||
expect(viewServer.data).toEqual({ description: e, lines: [] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('Edit Component', () => {
|
||||
|
@ -1,39 +1,22 @@
|
||||
import React from 'react'
|
||||
import renderer from 'react-test-renderer'
|
||||
import { Provider } from 'react-redux'
|
||||
import ConnectedComponent, { ShowFullContentButton } from '../../../components/ContentView/ShowFullContentButton'
|
||||
import ShowFullContentButton from '../../../components/ContentView/ShowFullContentButton'
|
||||
import { TStore } from '../../ducks/tutils'
|
||||
|
||||
|
||||
describe('ShowFullContentButton Component', () => {
|
||||
let store = TStore()
|
||||
|
||||
let setShowFullContentFn = jest.fn(),
|
||||
showFullContentButton = renderer.create(
|
||||
<ShowFullContentButton
|
||||
setShowFullContent={setShowFullContentFn}
|
||||
showFullContent={false}
|
||||
visibleLines={10}
|
||||
contentLines={20}
|
||||
/>
|
||||
<Provider store={store}>
|
||||
<ShowFullContentButton />
|
||||
</Provider>
|
||||
),
|
||||
tree = showFullContentButton.toJSON()
|
||||
|
||||
it('should render correctly', () => {
|
||||
expect(tree).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should handle click', () => {
|
||||
tree.children[0].props.onClick()
|
||||
expect(setShowFullContentFn).toBeCalled()
|
||||
})
|
||||
|
||||
it('should connect to state', () => {
|
||||
let store = TStore(),
|
||||
provider = renderer.create(
|
||||
<Provider store={store}>
|
||||
<ConnectedComponent/>
|
||||
</Provider>
|
||||
),
|
||||
tree = provider.toJSON()
|
||||
expect(tree).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
@ -3,24 +3,13 @@
|
||||
exports[`ContentViewOptions Component should render correctly 1`] = `
|
||||
<div
|
||||
className="view-options"
|
||||
>
|
||||
<a
|
||||
className="btn btn-default btn-xs pull-left"
|
||||
href="#"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span>
|
||||
<b>
|
||||
View:
|
||||
</b>
|
||||
|
||||
auto
|
||||
|
||||
<span
|
||||
className="caret"
|
||||
/>
|
||||
edit
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
className="btn btn-default btn-xs"
|
||||
@ -32,9 +21,21 @@ exports[`ContentViewOptions Component should render correctly 1`] = `
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
className="btn btn-default btn-xs"
|
||||
href="#"
|
||||
onClick={[Function]}
|
||||
title="Upload a file to replace the content."
|
||||
>
|
||||
<i
|
||||
className="fa fa-fw fa-upload"
|
||||
/>
|
||||
<input
|
||||
className="hidden"
|
||||
onChange={[Function]}
|
||||
type="file"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<span>
|
||||
foo
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
@ -34,19 +34,6 @@ exports[`ViewServer Component should render correctly and connect to state 1`] =
|
||||
|
||||
exports[`ViewServer Component should render correctly and connect to state 2`] = `
|
||||
<div>
|
||||
<pre>
|
||||
<div>
|
||||
<span
|
||||
className="k"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
<span
|
||||
className="v"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
</div>
|
||||
</pre>
|
||||
<pre />
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,23 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ShowFullContentButton Component should connect to state 1`] = `null`;
|
||||
|
||||
exports[`ShowFullContentButton Component should render correctly 1`] = `
|
||||
<div>
|
||||
<button
|
||||
className="view-all-content-btn btn-xs btn btn-default"
|
||||
onClick={[Function]}
|
||||
>
|
||||
Show full content
|
||||
</button>
|
||||
<span
|
||||
className="pull-right"
|
||||
>
|
||||
|
||||
10
|
||||
/
|
||||
20
|
||||
are visible
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
exports[`ShowFullContentButton Component should render correctly 1`] = `null`;
|
||||
|
@ -71,9 +71,7 @@ describe('Request Component', () => {
|
||||
})
|
||||
|
||||
it('should handle uploadContent on flow request ContentViewOptions', () => {
|
||||
// The line below shouldn't have .type, this is a workaround for https://github.com/facebook/react/issues/17301.
|
||||
// If this test breaks, just remove it.
|
||||
let contentViewOptions = provider.root.findByType(ContentViewOptions.type)
|
||||
let contentViewOptions = provider.root.findByType(ContentViewOptions)
|
||||
contentViewOptions.props.uploadContent('foo')
|
||||
expect(fetch).toBeCalled()
|
||||
fetch.mockClear()
|
||||
@ -132,9 +130,7 @@ describe('Response Component', () => {
|
||||
})
|
||||
|
||||
it('should handle updateContent on flow response ContentViewOptions', () => {
|
||||
// The line below shouldn't have .type, this is a workaround for https://github.com/facebook/react/issues/17301.
|
||||
// If this test breaks, just remove it.
|
||||
let contentViewOptions = provider.root.findByType(ContentViewOptions.type)
|
||||
let contentViewOptions = provider.root.findByType(ContentViewOptions)
|
||||
contentViewOptions.props.uploadContent('foo')
|
||||
expect(fetch).toBeCalled()
|
||||
fetch.mockClear()
|
||||
|
@ -195,24 +195,13 @@ exports[`Request Component should render correctly 1`] = `
|
||||
<footer>
|
||||
<div
|
||||
className="view-options"
|
||||
>
|
||||
<a
|
||||
className="btn btn-default btn-xs pull-left"
|
||||
href="#"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span>
|
||||
<b>
|
||||
View:
|
||||
</b>
|
||||
|
||||
auto
|
||||
|
||||
<span
|
||||
className="caret"
|
||||
/>
|
||||
edit
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
className="btn btn-default btn-xs"
|
||||
@ -224,10 +213,22 @@ exports[`Request Component should render correctly 1`] = `
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
className="btn btn-default btn-xs"
|
||||
href="#"
|
||||
onClick={[Function]}
|
||||
title="Upload a file to replace the content."
|
||||
>
|
||||
<i
|
||||
className="fa fa-fw fa-upload"
|
||||
/>
|
||||
<input
|
||||
className="hidden"
|
||||
onChange={[Function]}
|
||||
type="file"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<span>
|
||||
foo
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
@ -455,24 +456,13 @@ exports[`Response Component should render correctly 1`] = `
|
||||
<footer>
|
||||
<div
|
||||
className="view-options"
|
||||
>
|
||||
<a
|
||||
className="btn btn-default btn-xs pull-left"
|
||||
href="#"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span>
|
||||
<b>
|
||||
View:
|
||||
</b>
|
||||
|
||||
auto
|
||||
|
||||
<span
|
||||
className="caret"
|
||||
/>
|
||||
edit
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
className="btn btn-default btn-xs"
|
||||
@ -484,10 +474,22 @@ exports[`Response Component should render correctly 1`] = `
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
className="btn btn-default btn-xs"
|
||||
href="#"
|
||||
onClick={[Function]}
|
||||
title="Upload a file to replace the content."
|
||||
>
|
||||
<i
|
||||
className="fa fa-fw fa-upload"
|
||||
/>
|
||||
<input
|
||||
className="hidden"
|
||||
onChange={[Function]}
|
||||
type="file"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<span>
|
||||
foo
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
|
@ -21,7 +21,7 @@ type ResultProps = {
|
||||
results: CommandResult[],
|
||||
}
|
||||
|
||||
function getAvailableCommands(commands, input: string = "") {
|
||||
function getAvailableCommands(commands: object, input: string = "") {
|
||||
if (!commands) return []
|
||||
let availableCommands: string[] = []
|
||||
for (const [command, args] of Object.entries(commands)) {
|
||||
@ -37,7 +37,7 @@ export function Results({results}: ResultProps) {
|
||||
|
||||
useEffect(() => {
|
||||
if (resultElement) {
|
||||
resultElement.current?.addEventListener('DOMNodeInserted', event => {
|
||||
resultElement.current.addEventListener('DOMNodeInserted', event => {
|
||||
const { currentTarget: target } = event;
|
||||
target.scroll({ top: target.scrollHeight, behavior: 'auto' });
|
||||
});
|
||||
@ -83,7 +83,7 @@ export default function CommandBar() {
|
||||
const [completionCandidate, setCompletionCandidate] = useState<string[]>([])
|
||||
|
||||
const [availableCommands, setAvailableCommands] = useState<string[]>([])
|
||||
const [allCommands, setAllCommands] = useState({})
|
||||
const [allCommands, setAllCommands] = useState<object>({})
|
||||
const [nextArgs, setNextArgs] = useState<string[]>([])
|
||||
const [currentArg, setCurrentArg] = useState<number>(0)
|
||||
const [signatureHelp, setSignatureHelp] = useState<string>("")
|
||||
|
@ -1,19 +1,26 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { MessageUtils } from '../../flow/utils'
|
||||
|
||||
type ContentLoaderProps = {
|
||||
content: string,
|
||||
contentView: object,
|
||||
flow: object,
|
||||
message: {
|
||||
content: string,
|
||||
contentHash: string,
|
||||
},
|
||||
}
|
||||
|
||||
type ContentLoaderStates = {
|
||||
content: string | undefined,
|
||||
request: { abort: () => void }| undefined,
|
||||
}
|
||||
|
||||
export default function withContentLoader(View) {
|
||||
|
||||
return class extends React.Component {
|
||||
static displayName = View.displayName || View.name
|
||||
static matches = View.matches
|
||||
|
||||
static propTypes = {
|
||||
...View.propTypes,
|
||||
content: PropTypes.string, // mark as non-required
|
||||
flow: PropTypes.object.isRequired,
|
||||
message: PropTypes.object.isRequired,
|
||||
}
|
||||
return class extends React.Component<ContentLoaderProps, ContentLoaderStates> {
|
||||
static displayName: string = View.displayName || View.name
|
||||
static matches: (message: any) => boolean = View.matches
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
@ -45,6 +52,7 @@ export default function withContentLoader(View) {
|
||||
|
||||
updateContent(props) {
|
||||
if (this.state.request) {
|
||||
console.log("request:",this.state.request)
|
||||
this.state.request.abort()
|
||||
}
|
||||
// We have a few special cases where we do not need to make an HTTP request.
|
@ -1,32 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import ViewSelector from './ViewSelector'
|
||||
import UploadContentButton from './UploadContentButton'
|
||||
import DownloadContentButton from './DownloadContentButton'
|
||||
|
||||
ContentViewOptions.propTypes = {
|
||||
flow: PropTypes.object.isRequired,
|
||||
message: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
function ContentViewOptions({ flow, message, uploadContent, readonly, contentViewDescription }) {
|
||||
return (
|
||||
<div className="view-options">
|
||||
{readonly ? <ViewSelector message={message}/> : <span><b>View:</b> edit</span>}
|
||||
|
||||
<DownloadContentButton flow={flow} message={message}/>
|
||||
|
||||
{!readonly && <UploadContentButton uploadContent={uploadContent}/> }
|
||||
|
||||
{readonly && <span>{contentViewDescription}</span>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => ({
|
||||
contentViewDescription: state.ui.flow.viewDescription,
|
||||
readonly: !state.ui.flow.modifiedFlow,
|
||||
})
|
||||
)(ContentViewOptions)
|
27
web/src/js/components/ContentView/ContentViewOptions.tsx
Normal file
27
web/src/js/components/ContentView/ContentViewOptions.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react'
|
||||
import ViewSelector from './ViewSelector'
|
||||
import UploadContentButton from './UploadContentButton'
|
||||
import DownloadContentButton from './DownloadContentButton'
|
||||
import { useAppSelector } from "../../ducks";
|
||||
|
||||
type ContentViewOptionsProps = {
|
||||
flow: object,
|
||||
message: object,
|
||||
uploadContent: () => void,
|
||||
}
|
||||
|
||||
export default function ContentViewOptions({ flow, message, uploadContent }: ContentViewOptionsProps) {
|
||||
const contentViewDescription = useAppSelector(state => state.ui.flow.viewDescription)
|
||||
const readonly = useAppSelector(state => state.ui.flow.modifiedFlow);
|
||||
return (
|
||||
<div className="view-options">
|
||||
{readonly ? <ViewSelector /> : <span><b>View:</b> edit</span>}
|
||||
|
||||
<DownloadContentButton flow={flow} message={message}/>
|
||||
|
||||
{!readonly && <UploadContentButton uploadContent={uploadContent}/> }
|
||||
|
||||
{readonly && <span>{contentViewDescription}</span>}
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { setContentViewDescription, setContent } from '../../ducks/ui/flow'
|
||||
import withContentLoader from './ContentLoader'
|
||||
import { MessageUtils } from '../../flow/utils'
|
||||
import CodeEditor from './CodeEditor'
|
||||
|
||||
|
||||
const isImage = /^image\/(png|jpe?g|gif|webp|vnc.microsoft.icon|x-icon)$/i
|
||||
ViewImage.matches = msg => isImage.test(MessageUtils.getContentType(msg))
|
||||
ViewImage.propTypes = {
|
||||
flow: PropTypes.object.isRequired,
|
||||
message: PropTypes.object.isRequired,
|
||||
}
|
||||
function ViewImage({ flow, message }) {
|
||||
return (
|
||||
<div className="flowview-image">
|
||||
<img src={MessageUtils.getContentURL(flow, message)} alt="preview" className="img-thumbnail"/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Edit.propTypes = {
|
||||
content: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
function Edit({ content, onChange }) {
|
||||
return <CodeEditor content={content} onChange={onChange}/>
|
||||
}
|
||||
Edit = withContentLoader(Edit)
|
||||
|
||||
export class PureViewServer extends Component {
|
||||
static propTypes = {
|
||||
showFullContent: PropTypes.bool.isRequired,
|
||||
maxLines: PropTypes.number.isRequired,
|
||||
setContentViewDescription : PropTypes.func.isRequired,
|
||||
setContent: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount(){
|
||||
this.setContentView(this.props)
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps){
|
||||
if (nextProps.content !== this.props.content) {
|
||||
this.setContentView(nextProps)
|
||||
}
|
||||
}
|
||||
|
||||
setContentView(props){
|
||||
try {
|
||||
this.data = JSON.parse(props.content)
|
||||
}catch(err) {
|
||||
this.data = {lines: [], description: err.message}
|
||||
}
|
||||
|
||||
props.setContentViewDescription(props.contentView !== this.data.description ? this.data.description : '')
|
||||
props.setContent(this.data.lines)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {content, contentView, message, maxLines} = this.props
|
||||
let lines = this.props.showFullContent ? this.data.lines : this.data.lines.slice(0, maxLines)
|
||||
return (
|
||||
<div>
|
||||
{ViewImage.matches(message) && <ViewImage {...this.props} />}
|
||||
<pre>
|
||||
{lines.map((line, i) =>
|
||||
<div key={`line${i}`}>
|
||||
{line.map((element, j) => {
|
||||
let [style, text] = element
|
||||
return (
|
||||
<span key={`tuple${j}`} className={style}>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</pre>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const ViewServer = connect(
|
||||
state => ({
|
||||
showFullContent: state.ui.flow.showFullContent,
|
||||
maxLines: state.ui.flow.maxContentLines
|
||||
}),
|
||||
{
|
||||
setContentViewDescription,
|
||||
setContent
|
||||
}
|
||||
)(withContentLoader(PureViewServer))
|
||||
|
||||
export { Edit, ViewServer, ViewImage }
|
97
web/src/js/components/ContentView/ContentViews.tsx
Normal file
97
web/src/js/components/ContentView/ContentViews.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { setContentViewDescription, setContent } from '../../ducks/ui/flow'
|
||||
import withContentLoader from './ContentLoader'
|
||||
import { MessageUtils } from '../../flow/utils'
|
||||
import CodeEditor from './CodeEditor'
|
||||
import { useAppDispatch, useAppSelector } from "../../ducks";
|
||||
|
||||
|
||||
const isImage = /^image\/(png|jpe?g|gif|webp|vnc.microsoft.icon|x-icon)$/i
|
||||
ViewImage.matches = msg => isImage.test(MessageUtils.getContentType(msg))
|
||||
|
||||
type ViewImageProps = {
|
||||
flow: object,
|
||||
message: object,
|
||||
}
|
||||
|
||||
function ViewImage({ flow, message }: ViewImageProps) {
|
||||
return (
|
||||
<div className="flowview-image">
|
||||
<img src={MessageUtils.getContentURL(flow, message)} alt="preview" className="img-thumbnail"/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type EditProps = {
|
||||
content: string,
|
||||
onChange: () => void,
|
||||
}
|
||||
|
||||
function PureEdit({ content, onChange }: EditProps) {
|
||||
return <CodeEditor content={content} onChange={onChange}/>
|
||||
}
|
||||
const Edit = withContentLoader(PureEdit)
|
||||
|
||||
type PureViewServerProps = {
|
||||
flow: object,
|
||||
message: object,
|
||||
content: string,
|
||||
}
|
||||
|
||||
type PureViewServerStates = {
|
||||
lines: string[][],
|
||||
description: string,
|
||||
}
|
||||
|
||||
export function PureViewServer({flow, message, content}: PureViewServerProps) {
|
||||
const [data, setData] = useState<PureViewServerStates>({
|
||||
lines: [],
|
||||
description: "",
|
||||
})
|
||||
|
||||
const dispatch = useAppDispatch(),
|
||||
showFullContent: boolean = useAppSelector(state => state.ui.flow.showFullContent),
|
||||
maxLines: number = useAppSelector(state => state.ui.flow.maxContentLines)
|
||||
|
||||
let lines = showFullContent ? data.lines : data.lines?.slice(0, maxLines)
|
||||
|
||||
useEffect(() => {
|
||||
setContentView({flow, message, content})
|
||||
}, [flow, message, content])
|
||||
|
||||
const setContentView = (props) => {
|
||||
try {
|
||||
setData(JSON.parse(props.content))
|
||||
}catch(err) {
|
||||
setData({lines: [], description: err.message})
|
||||
}
|
||||
|
||||
dispatch(setContentViewDescription(props.contentView !== data.description ? data.description : ''))
|
||||
dispatch(setContent(data.lines))
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ViewImage.matches(message) && <ViewImage flow={flow} message={message}/>}
|
||||
<pre>
|
||||
{lines.map((line, i) =>
|
||||
<div key={`line${i}`}>
|
||||
{line.map((element, j) => {
|
||||
let [style, text] = element
|
||||
return (
|
||||
<span key={`tuple${j}`} className={style}>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</pre>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
const ViewServer = withContentLoader(PureViewServer)
|
||||
|
||||
export { Edit, ViewServer, ViewImage }
|
@ -1,13 +1,12 @@
|
||||
import React from 'react'
|
||||
import { MessageUtils } from "../../flow/utils"
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
DownloadContentButton.propTypes = {
|
||||
flow: PropTypes.object.isRequired,
|
||||
message: PropTypes.object.isRequired,
|
||||
type DownloadContentButtonProps = {
|
||||
flow: object,
|
||||
message: object,
|
||||
}
|
||||
|
||||
export default function DownloadContentButton({ flow, message }) {
|
||||
export default function DownloadContentButton({ flow, message }: DownloadContentButtonProps) {
|
||||
|
||||
return (
|
||||
<a className="btn btn-default btn-xs"
|
@ -3,7 +3,18 @@ import { formatSize } from '../../utils'
|
||||
import UploadContentButton from './UploadContentButton'
|
||||
import DownloadContentButton from './DownloadContentButton'
|
||||
|
||||
export function ContentEmpty({ flow, message }) {
|
||||
interface ContentProps {
|
||||
flow: { request: object },
|
||||
message: { contentLength: number },
|
||||
}
|
||||
|
||||
interface ContentTooLargeProps extends ContentProps {
|
||||
onClick: () => void,
|
||||
uploadContent: () => any,
|
||||
}
|
||||
|
||||
|
||||
export function ContentEmpty({ flow, message }: ContentProps) {
|
||||
return (
|
||||
<div className="alert alert-info">
|
||||
No {flow.request === message ? 'request' : 'response'} content.
|
||||
@ -11,7 +22,7 @@ export function ContentEmpty({ flow, message }) {
|
||||
)
|
||||
}
|
||||
|
||||
export function ContentMissing({ flow, message }) {
|
||||
export function ContentMissing({ flow, message }: ContentProps) {
|
||||
return (
|
||||
<div className="alert alert-info">
|
||||
{flow.request === message ? 'Request' : 'Response'} content missing.
|
||||
@ -19,7 +30,7 @@ export function ContentMissing({ flow, message }) {
|
||||
)
|
||||
}
|
||||
|
||||
export function ContentTooLarge({ message, onClick, uploadContent, flow }) {
|
||||
export function ContentTooLarge({ message, onClick, uploadContent, flow }: ContentTooLargeProps) {
|
||||
return (
|
||||
<div>
|
||||
<div className="alert alert-warning">
|
@ -1,39 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { render } from 'react-dom';
|
||||
import Button from '../common/Button';
|
||||
import { setShowFullContent } from '../../ducks/ui/flow'
|
||||
|
||||
|
||||
|
||||
ShowFullContentButton.propTypes = {
|
||||
setShowFullContent: PropTypes.func.isRequired,
|
||||
showFullContent: PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
export function ShowFullContentButton ( {setShowFullContent, showFullContent, visibleLines, contentLines} ){
|
||||
|
||||
return (
|
||||
!showFullContent &&
|
||||
<div>
|
||||
<Button className="view-all-content-btn btn-xs" onClick={() => setShowFullContent()}>
|
||||
Show full content
|
||||
</Button>
|
||||
<span className="pull-right"> {visibleLines}/{contentLines} are visible </span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => ({
|
||||
showFullContent: state.ui.flow.showFullContent,
|
||||
visibleLines: state.ui.flow.maxContentLines,
|
||||
contentLines: state.ui.flow.content.length
|
||||
|
||||
}),
|
||||
{
|
||||
setShowFullContent
|
||||
}
|
||||
)(ShowFullContentButton)
|
||||
|
22
web/src/js/components/ContentView/ShowFullContentButton.tsx
Normal file
22
web/src/js/components/ContentView/ShowFullContentButton.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react'
|
||||
import Button from '../common/Button';
|
||||
import { setShowFullContent } from '../../ducks/ui/flow'
|
||||
import {useAppDispatch, useAppSelector} from "../../ducks";
|
||||
|
||||
export default function ShowFullContentButton() {
|
||||
const dispatch = useAppDispatch(),
|
||||
showFullContent = useAppSelector(state => state.ui.flow.showFullContent),
|
||||
visibleLines = useAppSelector(state => state.ui.flow.maxContentLines),
|
||||
contentLines = useAppSelector(state => state.ui.flow.content.length)
|
||||
|
||||
return (
|
||||
!showFullContent &&
|
||||
<div>
|
||||
<Button className="view-all-content-btn btn-xs" onClick={() => dispatch(setShowFullContent())}>
|
||||
Show full content
|
||||
</Button>
|
||||
<span className="pull-right"> {visibleLines}/{contentLines} are visible </span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import FileChooser from '../common/FileChooser'
|
||||
|
||||
UploadContentButton.propTypes = {
|
||||
uploadContent: PropTypes.func.isRequired,
|
||||
type UploadContentButtonProps = {
|
||||
uploadContent: () => any,
|
||||
}
|
||||
|
||||
export default function UploadContentButton({ uploadContent }) {
|
||||
export default function UploadContentButton({ uploadContent }: UploadContentButtonProps) {
|
||||
|
||||
return (
|
||||
<FileChooser
|
@ -1,15 +1,16 @@
|
||||
import React from "react"
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from "classnames"
|
||||
|
||||
Button.propTypes = {
|
||||
onClick: PropTypes.func.isRequired,
|
||||
children: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
type ButtonProps = {
|
||||
onClick: () => void,
|
||||
children?: React.ReactNode,
|
||||
icon?: string,
|
||||
disabled?: boolean,
|
||||
className?: string,
|
||||
title?: string,
|
||||
}
|
||||
|
||||
export default function Button({ onClick, children, icon, disabled, className, title }) {
|
||||
export default function Button({ onClick, children, icon, disabled, className, title }: ButtonProps) {
|
||||
return (
|
||||
<button className={classnames(className, 'btn btn-default')}
|
||||
onClick={disabled ? undefined : onClick}
|
@ -1,11 +1,11 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
DocsLink.propTypes = {
|
||||
resource: PropTypes.string.isRequired,
|
||||
type DocLinkProps = {
|
||||
children: React.ReactNode,
|
||||
resource: string
|
||||
}
|
||||
|
||||
export default function DocsLink({ children, resource }) {
|
||||
export default function DocsLink({ children, resource }: DocLinkProps) {
|
||||
let url = `https://docs.mitmproxy.org/stable/${resource}`
|
||||
return (
|
||||
<a target="_blank" href={url}>
|
@ -2,7 +2,7 @@ import React from "react";
|
||||
|
||||
type FileChooserProps = {
|
||||
icon: string
|
||||
text: string
|
||||
text?: string
|
||||
className?: string
|
||||
title?: string
|
||||
onOpenFile: (File) => void
|
||||
|
@ -1,5 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function HideInStatic({ children }) {
|
||||
return (window.MITMWEB_CONF && window.MITMWEB_CONF.static) ? null : [children]
|
||||
}
|
9
web/src/js/components/common/HideInStatic.tsx
Normal file
9
web/src/js/components/common/HideInStatic.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
type HideInStaticProps = {
|
||||
children: React.ReactNode,
|
||||
}
|
||||
|
||||
export default function HideInStatic({ children }: HideInStaticProps) {
|
||||
return (window.MITMWEB_CONF && window.MITMWEB_CONF.static) ? null : [children]
|
||||
}
|
@ -2,14 +2,24 @@ import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import classnames from 'classnames'
|
||||
|
||||
export default class Splitter extends Component {
|
||||
type SplitterStates = {
|
||||
applied: boolean,
|
||||
startX: number,
|
||||
startY: number,
|
||||
}
|
||||
|
||||
type SplitterProps = {
|
||||
axis: string,
|
||||
}
|
||||
|
||||
export default class Splitter extends Component<SplitterProps, SplitterStates> {
|
||||
|
||||
static defaultProps = { axis: 'x' }
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
|
||||
this.state = { applied: false, startX: false, startY: false }
|
||||
this.state = { applied: false, startX: 0, startY: 0 }
|
||||
|
||||
this.onMouseMove = this.onMouseMove.bind(this)
|
||||
this.onMouseDown = this.onMouseDown.bind(this)
|
@ -1,13 +1,12 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
ToggleButton.propTypes = {
|
||||
checked: PropTypes.bool.isRequired,
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
text: PropTypes.string.isRequired
|
||||
type ToggleButtonProps = {
|
||||
checked: boolean,
|
||||
onToggle: () => void,
|
||||
text: string
|
||||
}
|
||||
|
||||
export default function ToggleButton({ checked, onToggle, text }) {
|
||||
export default function ToggleButton({ checked, onToggle, text }: ToggleButtonProps) {
|
||||
return (
|
||||
<div className={"btn btn-toggle " + (checked ? "btn-primary" : "btn-default")} onClick={onToggle}>
|
||||
<i className={"fa fa-fw " + (checked ? "fa-check-square-o" : "fa-square-o")}/>
|
@ -1,19 +1,21 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import { Key } from '../../utils'
|
||||
|
||||
export default class ToggleInputButton extends Component {
|
||||
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
txt: PropTypes.string,
|
||||
onToggleChanged: PropTypes.func.isRequired,
|
||||
checked: PropTypes.bool.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
inputType: PropTypes.string
|
||||
type ToggleInputButtonProps = {
|
||||
name: string,
|
||||
txt: string,
|
||||
onToggleChanged: (string) => void,
|
||||
checked: boolean,
|
||||
placeholder: string,
|
||||
inputType: string,
|
||||
}
|
||||
|
||||
type ToggleInputButtonStates = {
|
||||
txt: string,
|
||||
}
|
||||
|
||||
export default class ToggleInputButton extends Component<ToggleInputButtonProps, ToggleInputButtonStates> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = { txt: props.txt || '' }
|
Loading…
Reference in New Issue
Block a user