mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 22:30:15 +00:00
fixes ContentTypeDropdown and AuthDropdown (#4918)
This commit is contained in:
parent
f00dc50da0
commit
9bea9e06f1
@ -146,9 +146,9 @@ interface State {
|
||||
}
|
||||
|
||||
const isComponent = (match: string) => (child: ReactNode) => any(equals(match), [
|
||||
// @ts-expect-error not sure
|
||||
// @ts-expect-error this is required by our API for Dropdown
|
||||
child.type.name,
|
||||
// @ts-expect-error not sure
|
||||
// @ts-expect-error this is required by our API for Dropdown
|
||||
child.type.displayName,
|
||||
]);
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import classnames from 'classnames';
|
||||
import { any, equals } from 'ramda';
|
||||
import React, { CSSProperties, Fragment, PureComponent, ReactNode } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
@ -36,6 +37,17 @@ interface State {
|
||||
uniquenessKey: number;
|
||||
}
|
||||
|
||||
const isComponent = (match: string) => (child: ReactNode) => any(equals(match), [
|
||||
// @ts-expect-error this is required by our API for Dropdown
|
||||
child.type.name,
|
||||
// @ts-expect-error this is required by our API for Dropdown
|
||||
child.type.displayName,
|
||||
]);
|
||||
|
||||
const isDropdownItem = isComponent(DropdownItem.name);
|
||||
const isDropdownButton = isComponent(DropdownButton.name);
|
||||
const isDropdownDivider = isComponent(DropdownDivider.name);
|
||||
|
||||
@autoBindMethodsForReact(AUTOBIND_CFG)
|
||||
export class Dropdown extends PureComponent<DropdownProps, State> {
|
||||
private _node: HTMLDivElement | null = null;
|
||||
@ -356,8 +368,7 @@ export class Dropdown extends PureComponent<DropdownProps, State> {
|
||||
const allChildren = this._getFlattenedChildren(children);
|
||||
|
||||
const visibleChildren = allChildren.filter((child, i) => {
|
||||
// @ts-expect-error -- TSCONVERSION this should cater for all types that ReactNode can be
|
||||
if (child.type.name !== DropdownItem.name) {
|
||||
if (isDropdownItem(child)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -368,11 +379,9 @@ export class Dropdown extends PureComponent<DropdownProps, State> {
|
||||
for (let i = 0; i < allChildren.length; i++) {
|
||||
const child = allChildren[i];
|
||||
|
||||
// @ts-expect-error -- TSCONVERSION this should cater for all types that ReactNode can be
|
||||
if (child.type.name === DropdownButton.name) {
|
||||
if (isDropdownButton(child)) {
|
||||
dropdownButtons.push(child);
|
||||
// @ts-expect-error -- TSCONVERSION this should cater for all types that ReactNode can be
|
||||
} else if (child.type.name === DropdownItem.name) {
|
||||
} else if (isDropdownItem(child)) {
|
||||
const active = i === filterActiveIndex;
|
||||
const hide = !visibleChildren.includes(child);
|
||||
dropdownItems.push(
|
||||
@ -387,14 +396,12 @@ export class Dropdown extends PureComponent<DropdownProps, State> {
|
||||
{child}
|
||||
</li>,
|
||||
);
|
||||
// @ts-expect-error -- TSCONVERSION this should cater for all types that ReactNode can be
|
||||
} else if (child.type.name === DropdownDivider.name) {
|
||||
} else if (isDropdownDivider(child)) {
|
||||
const currentIndex = visibleChildren.indexOf(child);
|
||||
const nextChild = visibleChildren[currentIndex + 1];
|
||||
|
||||
// Only show the divider if the next child is a DropdownItem
|
||||
// @ts-expect-error -- TSCONVERSION this should cater for all types that ReactNode can be
|
||||
if (nextChild && nextChild.type.name === DropdownItem.name) {
|
||||
if (nextChild && isDropdownItem(nextChild)) {
|
||||
dropdownItems.push(<li key={i}>{child}</li>);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { FC, useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import {
|
||||
AUTH_ASAP,
|
||||
@ -16,6 +17,7 @@ import {
|
||||
} from '../../../common/constants';
|
||||
import * as models from '../../../models';
|
||||
import type { Request, RequestAuthentication } from '../../../models/request';
|
||||
import { selectActiveRequest } from '../../redux/selectors';
|
||||
import { Dropdown } from '../base/dropdown/dropdown';
|
||||
import { DropdownButton } from '../base/dropdown/dropdown-button';
|
||||
import { DropdownDivider } from '../base/dropdown/dropdown-divider';
|
||||
@ -34,17 +36,27 @@ const AuthItem: FC<{
|
||||
{nameOverride || getAuthTypeName(type, true)}
|
||||
</DropdownItem>
|
||||
);
|
||||
AuthItem.displayName = DropdownItem.name;
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
onChange: (request: Request, arg1: RequestAuthentication) => Promise<Request>;
|
||||
request: Request;
|
||||
}
|
||||
|
||||
export const AuthDropdown: FC<Props> = ({ children, className, onChange, request }) => {
|
||||
const { authentication } = request;
|
||||
export const AuthDropdown: FC<Props> = ({ onChange }) => {
|
||||
const activeRequest = useSelector(selectActiveRequest);
|
||||
|
||||
const onClick = useCallback((type: string) => {
|
||||
if (!activeRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!('authentication' in activeRequest)) {
|
||||
// gRPC Requests don't have `authentication`
|
||||
return;
|
||||
}
|
||||
|
||||
const { authentication } = activeRequest;
|
||||
|
||||
const fn = async () => {
|
||||
if (type === authentication.type) {
|
||||
// Type didn't change
|
||||
@ -73,21 +85,34 @@ export const AuthDropdown: FC<Props> = ({ children, className, onChange, request
|
||||
break;
|
||||
}
|
||||
}
|
||||
onChange(request, newAuthentication);
|
||||
onChange(activeRequest, newAuthentication);
|
||||
};
|
||||
fn();
|
||||
}, [authentication, onChange, request]);
|
||||
}, [onChange, activeRequest]);
|
||||
|
||||
const isCurrent = useCallback((type: string) => (
|
||||
type === (authentication.type || AUTH_NONE)
|
||||
), [authentication.type]);
|
||||
const isCurrent = useCallback((type: string) => {
|
||||
if (!activeRequest) {
|
||||
return false;
|
||||
}
|
||||
if (!('authentication' in activeRequest)) {
|
||||
return false;
|
||||
}
|
||||
return type === (activeRequest.authentication.type || AUTH_NONE);
|
||||
}, [activeRequest]);
|
||||
|
||||
if (!activeRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const itemProps = { onClick, isCurrent };
|
||||
|
||||
return (
|
||||
<Dropdown beside>
|
||||
<DropdownDivider>Auth Types</DropdownDivider>
|
||||
<DropdownButton className={className}>{children}</DropdownButton>
|
||||
<DropdownButton className="tall">
|
||||
{'authentication' in activeRequest ? getAuthTypeName(activeRequest.authentication.type) || 'Auth' : 'Auth'}
|
||||
<i className="fa fa-caret-down space-left" />
|
||||
</DropdownButton>
|
||||
<AuthItem type={AUTH_BASIC} {...itemProps} />
|
||||
<AuthItem type={AUTH_DIGEST} {...itemProps} />
|
||||
<AuthItem type={AUTH_OAUTH_1} {...itemProps} />
|
||||
|
@ -16,16 +16,15 @@ import {
|
||||
getContentTypeName,
|
||||
} from '../../../common/constants';
|
||||
import { selectActiveRequest } from '../../redux/selectors';
|
||||
import { Dropdown, DropdownProps } from '../base/dropdown/dropdown';
|
||||
import { Dropdown } from '../base/dropdown/dropdown';
|
||||
import { DropdownButton } from '../base/dropdown/dropdown-button';
|
||||
import { DropdownDivider } from '../base/dropdown/dropdown-divider';
|
||||
import { DropdownItem } from '../base/dropdown/dropdown-item';
|
||||
import { AlertModal } from '../modals/alert-modal';
|
||||
import { showModal } from '../modals/index';
|
||||
|
||||
interface Props extends DropdownProps {
|
||||
interface Props {
|
||||
onChange: (mimeType: string | null) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const EMPTY_MIME_TYPE = null;
|
||||
@ -41,36 +40,38 @@ const MimeTypeItem: FC<{
|
||||
}) => {
|
||||
const activeRequest = useSelector(selectActiveRequest);
|
||||
const handleChangeMimeType = useCallback(async (mimeType: string | null) => {
|
||||
if (activeRequest) {
|
||||
const { body } = activeRequest;
|
||||
const hasMimeType = 'mimeType' in body;
|
||||
if (hasMimeType && body.mimeType === mimeType) {
|
||||
// Nothing to do since the mimeType hasn't changed
|
||||
return;
|
||||
}
|
||||
if (!activeRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasParams = 'params' in body && body.params && body.params.length;
|
||||
const hasText = body.text && body.text.length;
|
||||
const hasFile = 'fileName' in body && body.fileName && body.fileName.length;
|
||||
const isEmpty = !hasParams && !hasText && !hasFile;
|
||||
const isFile = hasMimeType && body.mimeType === CONTENT_TYPE_FILE;
|
||||
const isMultipart = hasMimeType && body.mimeType === CONTENT_TYPE_FORM_DATA;
|
||||
const isFormUrlEncoded = hasMimeType && body.mimeType === CONTENT_TYPE_FORM_URLENCODED;
|
||||
const isText = !isFile && !isMultipart;
|
||||
const willBeFile = mimeType === CONTENT_TYPE_FILE;
|
||||
const willBeMultipart = mimeType === CONTENT_TYPE_FORM_DATA;
|
||||
const willBeGraphQL = mimeType === CONTENT_TYPE_GRAPHQL;
|
||||
const willConvertToText = !willBeGraphQL && !willBeFile && !willBeMultipart;
|
||||
const willPreserveText = willConvertToText && isText;
|
||||
const willPreserveForm = isFormUrlEncoded && willBeMultipart;
|
||||
const { body } = activeRequest;
|
||||
const hasMimeType = 'mimeType' in body;
|
||||
if (hasMimeType && body.mimeType === mimeType) {
|
||||
// Nothing to do since the mimeType hasn't changed
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isEmpty && !willPreserveText && !willPreserveForm) {
|
||||
await showModal(AlertModal, {
|
||||
title: 'Switch Body Type?',
|
||||
message: 'Current body will be lost. Are you sure you want to continue?',
|
||||
addCancel: true,
|
||||
});
|
||||
}
|
||||
const hasParams = 'params' in body && body.params && body.params.length;
|
||||
const hasText = body.text && body.text.length;
|
||||
const hasFile = 'fileName' in body && body.fileName && body.fileName.length;
|
||||
const isEmpty = !hasParams && !hasText && !hasFile;
|
||||
const isFile = hasMimeType && body.mimeType === CONTENT_TYPE_FILE;
|
||||
const isMultipart = hasMimeType && body.mimeType === CONTENT_TYPE_FORM_DATA;
|
||||
const isFormUrlEncoded = hasMimeType && body.mimeType === CONTENT_TYPE_FORM_URLENCODED;
|
||||
const isText = !isFile && !isMultipart;
|
||||
const willBeFile = mimeType === CONTENT_TYPE_FILE;
|
||||
const willBeMultipart = mimeType === CONTENT_TYPE_FORM_DATA;
|
||||
const willBeGraphQL = mimeType === CONTENT_TYPE_GRAPHQL;
|
||||
const willConvertToText = !willBeGraphQL && !willBeFile && !willBeMultipart;
|
||||
const willPreserveText = willConvertToText && isText;
|
||||
const willPreserveForm = isFormUrlEncoded && willBeMultipart;
|
||||
|
||||
if (!isEmpty && !willPreserveText && !willPreserveForm) {
|
||||
await showModal(AlertModal, {
|
||||
title: 'Switch Body Type?',
|
||||
message: 'Current body will be lost. Are you sure you want to continue?',
|
||||
addCancel: true,
|
||||
});
|
||||
}
|
||||
|
||||
onChange(mimeType);
|
||||
@ -87,40 +88,52 @@ const MimeTypeItem: FC<{
|
||||
</DropdownItem>
|
||||
);
|
||||
};
|
||||
MimeTypeItem.displayName = DropdownItem.name;
|
||||
|
||||
export const ContentTypeDropdown: FC<Props> = ({
|
||||
children,
|
||||
className,
|
||||
onChange,
|
||||
...extraProps
|
||||
}) => (
|
||||
<Dropdown beside {...extraProps}>
|
||||
<DropdownButton className={className}>{children}</DropdownButton>
|
||||
<DropdownDivider>
|
||||
<span>
|
||||
<i className="fa fa-bars" /> Structured
|
||||
</span>
|
||||
</DropdownDivider>
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_FORM_DATA} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_FORM_URLENCODED} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_GRAPHQL} onChange={onChange} />
|
||||
<DropdownDivider>
|
||||
<span>
|
||||
<i className="fa fa-code" /> Text
|
||||
</span>
|
||||
</DropdownDivider>
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_JSON} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_XML} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_YAML} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_EDN} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_PLAINTEXT} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_OTHER} onChange={onChange} />
|
||||
<DropdownDivider>
|
||||
<span>
|
||||
<i className="fa fa-ellipsis-h" /> Other
|
||||
</span>
|
||||
</DropdownDivider>
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_FILE} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={EMPTY_MIME_TYPE} forcedName="No Body" onChange={onChange} />
|
||||
</Dropdown>
|
||||
);
|
||||
export const ContentTypeDropdown: FC<Props> = ({ onChange }) => {
|
||||
const activeRequest = useSelector(selectActiveRequest);
|
||||
|
||||
if (!activeRequest) {
|
||||
return null;
|
||||
}
|
||||
const { body } = activeRequest;
|
||||
const hasMimeType = 'mimeType' in body;
|
||||
const hasParams = body && 'params' in body && body.params;
|
||||
const numBodyParams = hasParams ? body.params?.filter(({ disabled }) => !disabled).length : 0;
|
||||
|
||||
return (
|
||||
<Dropdown>
|
||||
<DropdownButton className="tall">
|
||||
{hasMimeType ? getContentTypeName(body.mimeType) : 'Body'}
|
||||
{numBodyParams ? <span className="bubble space-left">{numBodyParams}</span> : null}
|
||||
<i className="fa fa-caret-down space-left" />
|
||||
</DropdownButton>
|
||||
<DropdownDivider>
|
||||
<span>
|
||||
<i className="fa fa-bars" /> Structured
|
||||
</span>
|
||||
</DropdownDivider>
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_FORM_DATA} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_FORM_URLENCODED} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_GRAPHQL} onChange={onChange} />
|
||||
<DropdownDivider>
|
||||
<span>
|
||||
<i className="fa fa-code" /> Text
|
||||
</span>
|
||||
</DropdownDivider>
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_JSON} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_XML} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_YAML} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_EDN} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_PLAINTEXT} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_OTHER} onChange={onChange} />
|
||||
<DropdownDivider>
|
||||
<span>
|
||||
<i className="fa fa-ellipsis-h" /> Other
|
||||
</span>
|
||||
</DropdownDivider>
|
||||
<MimeTypeItem mimeType={CONTENT_TYPE_FILE} onChange={onChange} />
|
||||
<MimeTypeItem mimeType={EMPTY_MIME_TYPE} forcedName="No Body" onChange={onChange} />
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
@ -4,7 +4,6 @@ import React, { FC, useCallback, useEffect, useRef } from 'react';
|
||||
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
|
||||
import { useMount } from 'react-use';
|
||||
|
||||
import { getAuthTypeName, getContentTypeName } from '../../../common/constants';
|
||||
import * as models from '../../../models';
|
||||
import { queryAllWorkspaceUrls } from '../../../models/helpers/query-all-workspace-urls';
|
||||
import type {
|
||||
@ -147,12 +146,6 @@ export const RequestPane: FC<Props> = ({
|
||||
);
|
||||
}
|
||||
|
||||
let numBodyParams = 0;
|
||||
|
||||
if (request.body && request.body.params) {
|
||||
numBodyParams = request.body.params.filter(p => !p.disabled).length;
|
||||
}
|
||||
|
||||
const numParameters = request.parameters.filter(p => !p.disabled).length;
|
||||
const numHeaders = request.headers.filter(h => !h.disabled).length;
|
||||
const urlHasQueryParameters = request.url.indexOf('?') >= 0;
|
||||
@ -185,24 +178,12 @@ export const RequestPane: FC<Props> = ({
|
||||
<Tab tabIndex="=1">
|
||||
<ContentTypeDropdown
|
||||
onChange={updateRequestMimeType}
|
||||
className="tall"
|
||||
>
|
||||
{typeof request.body.mimeType === 'string'
|
||||
? getContentTypeName(request.body.mimeType)
|
||||
: 'Body'}
|
||||
{numBodyParams ? <span className="bubble space-left">{numBodyParams}</span> : null}
|
||||
<i className="fa fa-caret-down space-left" />
|
||||
</ContentTypeDropdown>
|
||||
/>
|
||||
</Tab>
|
||||
<Tab tabIndex="=1">
|
||||
<AuthDropdown
|
||||
onChange={updateRequestAuthentication}
|
||||
request={request}
|
||||
className="tall"
|
||||
>
|
||||
{getAuthTypeName(request.authentication.type) || 'Auth'}
|
||||
<i className="fa fa-caret-down space-left" />
|
||||
</AuthDropdown>
|
||||
/>
|
||||
</Tab>
|
||||
<Tab tabIndex="=1">
|
||||
<button>
|
||||
|
Loading…
Reference in New Issue
Block a user