fix: API Editor bug fixes (#8803)

* * Confirmation modal fixes

* * header overflow fixed, API editor

* * Delete truncate issue fix
* Tabs count UI fix

* * Removed hardcoded text from component and added in messages

* * removed hardcoded size

* * fixed scroll issue in appviewer

* * eval field fixes

* * design fix for key in API pane

* * fixed test cases
This commit is contained in:
albinAppsmith 2021-11-01 10:24:06 +05:30 committed by GitHub
parent 2e2e0d1d51
commit 55df317211
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 177 additions and 56 deletions

View File

@ -32,7 +32,7 @@ describe("Confirm run action", function() {
cy.get(queryEditor.runQuery).click();
cy.get(".bp3-dialog")
.find(".bp3-button")
.find("button")
.contains("Confirm")
.click();
cy.wait("@postExecute").should(

View File

@ -90,11 +90,14 @@ export const TabTitle = styled.span`
export const TabCount = styled.div`
background-color: ${(props) => props.theme.colors.tabs.countBg};
border-radius: 8px;
width: 17px;
min-width: 17px;
height: 17px;
font-size: 9px;
line-height: 14px;
margin-left: 2px;
margin-left: 4px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 2px;
`;
const TabTitleWrapper = styled.div<{

View File

@ -85,7 +85,7 @@ const ContentWrapper = styled.div<{ colorTheme: EditorTheme }>`
const CurrentValueWrapper = styled.div<{ colorTheme: EditorTheme }>`
max-height: 300px;
min-height: 1rem;
min-height: 28px;
overflow-y: auto;
-ms-overflow-style: none;
padding: ${(props) => props.theme.spaces[3]}px;
@ -101,6 +101,7 @@ const CopyIconWrapper = styled(Button)<{ colorTheme: EditorTheme }>`
top: 0;
cursor: pointer;
padding: 0;
border-radius: 0;
`;
const CodeWrapper = styled.pre<{ colorTheme: EditorTheme }>`

View File

@ -120,10 +120,7 @@ export const EditorWrapper = styled.div<{
return Colors.GREY_5;
}
}};
background: ${(props) =>
props.isFocused
? props.theme.colors.apiPane.requestTree.header.bg
: props.theme.colors.apiPane.bg};
background: ${(props) => props.theme.colors.apiPane.bg};
color: ${Colors.CHARCOAL};
& {
span.cm-operator {

View File

@ -18,9 +18,11 @@ import DynamicDropdownField from "./DynamicDropdownField";
import {
DEFAULT_MULTI_PART_DROPDOWN_PLACEHOLDER,
DEFAULT_MULTI_PART_DROPDOWN_WIDTH,
DEFAULT_MULTI_PART_DROPDOWN_HEIGHT,
MULTI_PART_DROPDOWN_OPTIONS,
} from "constants/ApiEditorConstants";
import { Colors } from "constants/Colors";
import { Classes as BlueprintClasses } from "@blueprintjs/core";
type CustomStack = {
removeTopPadding?: boolean;
@ -107,12 +109,18 @@ const FlexContainer = styled.div`
const DynamicTextFieldWithDropdownWrapper = styled.div`
display: flex;
position: relative;
top: -2px;
&
.${BlueprintClasses.POPOVER_TARGET},
&
.${BlueprintClasses.POPOVER_TARGET}
> div {
height: 100%;
}
`;
const DynamicDropdownFieldWrapper = styled.div`
position: relative;
top: 1px;
margin-left: 5px;
`;
@ -176,7 +184,6 @@ function KeyValueRow(props: Props & WrappedFieldArrayProps) {
className={`t--${field}.key.${index}`}
dataTreePath={`${props.dataTreePath}[${index}].key`}
expected={expected}
height="36px"
hoverInteraction
name={`${field}.key`}
placeholder={`Key ${index + 1}`}
@ -185,7 +192,7 @@ function KeyValueRow(props: Props & WrappedFieldArrayProps) {
<DynamicDropdownFieldWrapper>
<DynamicDropdownField
height="36px"
height={DEFAULT_MULTI_PART_DROPDOWN_HEIGHT}
name={`${field}.type`}
options={MULTI_PART_DROPDOWN_OPTIONS}
placeholder={DEFAULT_MULTI_PART_DROPDOWN_PLACEHOLDER}
@ -199,7 +206,6 @@ function KeyValueRow(props: Props & WrappedFieldArrayProps) {
className={`t--${field}.key.${index}`}
dataTreePath={`${props.dataTreePath}[${index}].key`}
expected={expected}
height="36px"
hoverInteraction
name={`${field}.key`}
placeholder={`Key ${index + 1}`}
@ -215,7 +221,6 @@ function KeyValueRow(props: Props & WrappedFieldArrayProps) {
className={`t--${field}.value.${index}`}
dataTreePath={`${props.dataTreePath}[${index}].value`}
expected={expected}
height="36px"
hoverInteraction
name={`${field}.value`}
placeholder={`Value ${index + 1}`}
@ -236,7 +241,6 @@ function KeyValueRow(props: Props & WrappedFieldArrayProps) {
)
}
expected={expected}
height="36px"
name={`${field}.value`}
placeholder={
props.placeholder

View File

@ -26,7 +26,7 @@ const StyledDynamicTextField = styled(DynamicTextField)`
background-color: ${Colors.WHITE};
}
.CodeEditorTarget .CodeMirror.CodeMirror-wrap:hover {
background-color: inherit;
background-color: ${Colors.ALABASTER_ALT};
}
&&& .t--code-editor-wrapper {
border: none;

View File

@ -97,4 +97,5 @@ export const MULTI_PART_DROPDOWN_OPTIONS: MULTI_PART_DROPDOWN_OPTION[] = [
];
export const DEFAULT_MULTI_PART_DROPDOWN_WIDTH = "77px";
export const DEFAULT_MULTI_PART_DROPDOWN_HEIGHT = "100%";
export const DEFAULT_MULTI_PART_DROPDOWN_PLACEHOLDER = "Type";

View File

@ -2574,7 +2574,7 @@ export const light: ColorType = {
border: "#E0DEDE",
},
apiPane: {
bg: lightShades[0],
bg: lightShades[11],
tabBg: lightShades[11],
text: lightShades[16],
dividerBg: lightShades[13],

View File

@ -205,6 +205,7 @@ export const ReduxActionTypes = {
LOAD_QUERY_RESPONSE: "LOAD_QUERY_RESPONSE",
RUN_ACTION_SHORTCUT_REQUEST: "RUN_ACTION_SHORTCUT_REQUEST",
RUN_ACTION_REQUEST: "RUN_ACTION_REQUEST",
RUN_ACTION_CANCELLED: "RUN_ACTION_CANCELLED",
RUN_ACTION_SUCCESS: "RUN_ACTION_SUCCESS",
CLEAR_ACTION_RESPONSE: "CLEAR_ACTION_RESPONSE",
INIT_API_PANE: "INIT_API_PANE",

View File

@ -812,3 +812,5 @@ export const WELCOME_FORM_NON_SUPER_USER_ROLE_DROPDOWN = () =>
export const WELCOME_FORM_NON_SUPER_USER_ROLE = () => "Role";
export const WELCOME_FORM_NON_SUPER_USER_USE_CASE = () =>
"What are you planning to use Appsmith for?";
export const QUERY_CONFIRMATION_MODAL_MESSAGE = () =>
"Are you sure you want to perform this action?";

View File

@ -46,7 +46,8 @@ const AppViewerBody = styled.section<{ hasPages: boolean }>`
justify-content: flex-start;
height: calc(
100vh -
${(props) => (!props.hasPages ? props.theme.smallHeaderHeight : "72px")}
${(props) =>
!props.hasPages ? `${props.theme.smallHeaderHeight} - 1px` : "72px"}
);
`;

View File

@ -59,6 +59,10 @@ import { Colors } from "constants/Colors";
import SearchSnippets from "components/ads/SnippetButton";
import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
import ApiAuthentication from "./ApiAuthentication";
import TooltipComponent from "components/ads/Tooltip";
import { TOOLTIP_HOVER_ON_DELAY } from "constants/AppConstants";
import { Position } from "@blueprintjs/core/lib/esnext/common";
import { Classes as BluePrintClasses } from "@blueprintjs/core";
const Form = styled.form`
display: flex;
@ -290,6 +294,52 @@ const Flex = styled.div<{ size: number }>`
color: #4b4848;
display: flex;
align-items: center;
&.possible-overflow-key {
overflow: hidden;
text-overflow: ellipsis;
width: fit-content;
max-width: 100%;
.${BluePrintClasses.POPOVER_WRAPPER} {
width: fit-content;
max-width: 100%;
}
.${BluePrintClasses.POPOVER_TARGET} > span {
display: block;
overflow: hidden;
text-overflow: ellipsis;
width: fit-content;
max-width: 100%;
padding-right: 8px;
}
}
&.possible-overflow {
width: 0;
max-height: 32px;
& > span.cs-text {
width: 100%;
}
.${BluePrintClasses.POPOVER_TARGET} {
width: fit-content;
max-width: 100%;
}
.${BluePrintClasses.POPOVER_TARGET} > span {
max-height: 32px;
padding: 6px 12px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 2px;
width: fit-content;
max-width: 100%;
}
}
`;
const FlexContainer = styled.div`
@ -297,6 +347,10 @@ const FlexContainer = styled.div`
align-items: center;
width: calc(100% - 30px);
&.header {
margin-bottom: 8px;
}
.key-value {
.${Classes.TEXT} {
color: ${(props) => props.theme.colors.apiPane.text};
@ -337,11 +391,28 @@ function ImportedHeaderKeyValue(props: { headers: any }) {
return (
<FormRowWithLabel key={index}>
<FlexContainer>
<Flex className="key-value disabled" size={1}>
<Text type={TextType.H6}>{header.key}</Text>
<Flex
className="key-value disabled possible-overflow-key"
size={1}
>
<TooltipComponent
content={header.key}
hoverOpenDelay={TOOLTIP_HOVER_ON_DELAY}
position={Position.BOTTOM_LEFT}
>
<Text type={TextType.H6}>{header.key}</Text>
</TooltipComponent>
</Flex>
<Flex className="key-value disabled" size={3}>
<Text type={TextType.H6}>{header.value}</Text>
<Flex className="key-value disabled possible-overflow" size={3}>
<Text type={TextType.H6}>
<TooltipComponent
content={header.value}
hoverOpenDelay={TOOLTIP_HOVER_ON_DELAY}
position={Position.BOTTOM_LEFT}
>
{header.value}
</TooltipComponent>
</Text>
</Flex>
</FlexContainer>
</FormRowWithLabel>
@ -426,7 +497,7 @@ function ImportedHeaders(props: { headers: any }) {
)}
<KeyValueStackContainer>
<FormRowWithLabel>
<FlexContainer>
<FlexContainer className="header">
<Flex className="key-value" size={1}>
<Text case={Case.CAPITALIZE} type={TextType.H6}>
Key
@ -527,10 +598,10 @@ function ApiEditorForm(props: Props) {
className="t--apiFormHttpMethod"
height={"35px"}
name="actionConfiguration.httpMethod"
optionWidth={"102px"}
optionWidth={"110px"}
options={HTTP_METHOD_OPTIONS}
placeholder="Method"
width={"102px"}
width={"110px"}
/>
</BoundaryContainer>
<DatasourceWrapper className="t--dataSourceField">

View File

@ -1,19 +1,37 @@
import React from "react";
import { connect } from "react-redux";
import { AppState } from "reducers";
import { Dialog, Classes } from "@blueprintjs/core";
import Button from "components/editorComponents/Button";
import {
showRunActionConfirmModal,
cancelRunActionConfirmModal,
acceptRunActionConfirmModal,
} from "actions/pluginActionActions";
import DialogComponent from "components/ads/DialogComponent";
import styled from "styled-components";
import Button, { Category, Size } from "components/ads/Button";
import {
createMessage,
QUERY_CONFIRMATION_MODAL_MESSAGE,
} from "constants/messages";
type Props = {
isModalOpen: boolean;
dispatch: any;
};
const ModalBody = styled.div`
padding-bottom: 20px;
`;
const ModalFooter = styled.div`
display: flex;
justify-content: flex-end;
button {
margin-left: 12px;
}
`;
class ConfirmRunModal extends React.Component<Props> {
render() {
const { dispatch, isModalOpen } = this.props;
@ -24,34 +42,39 @@ class ConfirmRunModal extends React.Component<Props> {
};
return (
<Dialog isOpen={isModalOpen} onClose={handleClose} title="Confirm Action">
<div className={Classes.DIALOG_BODY}>
Are you sure you want to perform this action?
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button
filled
onClick={() => {
dispatch(cancelRunActionConfirmModal());
handleClose();
}}
text="Cancel"
/>
<Button
filled
intent="primary"
onClick={() => {
dispatch(acceptRunActionConfirmModal());
handleClose();
}}
text="Confirm"
/>
</div>
</div>
</Dialog>
<DialogComponent
isOpen={isModalOpen}
maxHeight={"80vh"}
onClose={handleClose}
title="Confirm Action"
width={"580px"}
>
<ModalBody>{createMessage(QUERY_CONFIRMATION_MODAL_MESSAGE)}</ModalBody>
<ModalFooter>
<Button
category={Category.tertiary}
onClick={() => {
dispatch(cancelRunActionConfirmModal());
handleClose();
}}
size={Size.medium}
tag="button"
text="Cancel"
type="button"
/>
<Button
category={Category.primary}
onClick={() => {
dispatch(acceptRunActionConfirmModal());
handleClose();
}}
size={Size.medium}
tag="button"
text="Confirm"
type="button"
/>
</ModalFooter>
</DialogComponent>
);
}
}

View File

@ -127,6 +127,19 @@ const queryPaneReducer = createReducer(initialState, {
};
},
[ReduxActionTypes.RUN_ACTION_CANCELLED]: (
state: any,
action: ReduxAction<{ id: string }>,
) => {
return {
...state,
isRunning: {
...state.isRunning,
[action.payload.id]: false,
},
};
},
[ReduxActionTypes.RUN_ACTION_SUCCESS]: (
state: any,
action: ReduxAction<{ [id: string]: ActionResponse }>,

View File

@ -665,6 +665,10 @@ function* executePluginActionSaga(
if (pluginAction.confirmBeforeExecute) {
const confirmed = yield call(confirmRunActionSaga);
if (!confirmed) {
yield put({
type: ReduxActionTypes.RUN_ACTION_CANCELLED,
payload: { id: actionId },
});
throw new UserCancelledActionExecutionError();
}
}