fix: depercate form button widget (#14510)
This commit is contained in:
parent
6ad308ec39
commit
f45a15545d
|
|
@ -81,7 +81,7 @@ describe("Theme validation usecases", function() {
|
|||
.then(($childElem) => {
|
||||
cy.get($childElem).click({ force: true });
|
||||
cy.get(
|
||||
".t--draggable-formbuttonwidget button :contains('Submit')",
|
||||
".t--draggable-buttonwidget button :contains('Submit')",
|
||||
).should(
|
||||
"have.css",
|
||||
"font-family",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
|||
import { CodeEditorExpected } from "components/editorComponents/CodeEditor";
|
||||
import { UpdateWidgetPropertyPayload } from "actions/controlActions";
|
||||
import { AppTheme } from "entities/AppTheming";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
|
||||
const ControlTypes = getPropertyControlTypes();
|
||||
export type ControlType = typeof ControlTypes[keyof typeof ControlTypes];
|
||||
|
|
@ -15,7 +16,11 @@ export type PropertyPaneSectionConfig = {
|
|||
sectionName: string;
|
||||
id?: string;
|
||||
children: PropertyPaneConfig[];
|
||||
hidden?: (props: any, propertyPath: string) => boolean;
|
||||
hidden?: (
|
||||
props: any,
|
||||
propertyPath: string,
|
||||
widgetParentProps?: WidgetProps,
|
||||
) => boolean;
|
||||
isDefaultOpen?: boolean;
|
||||
propertySectionPath?: string;
|
||||
};
|
||||
|
|
@ -61,7 +66,11 @@ export type PropertyPaneControlConfig = {
|
|||
propertyName: string,
|
||||
propertyValue: any,
|
||||
) => Array<PropertyHookUpdates> | undefined;
|
||||
hidden?: (props: any, propertyPath: string) => boolean;
|
||||
hidden?: (
|
||||
props: any,
|
||||
propertyPath: string,
|
||||
widgetParentProps?: WidgetProps,
|
||||
) => boolean;
|
||||
invisible?: boolean;
|
||||
isBindProperty: boolean;
|
||||
isTriggerProperty: boolean;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import { TooltipComponent } from "design-system";
|
|||
import { ReactComponent as ResetIcon } from "assets/icons/control/undo_2.svg";
|
||||
import { AppTheme } from "entities/AppTheming";
|
||||
import { JS_TOGGLE_DISABLED_MESSAGE } from "@appsmith/constants/messages";
|
||||
import { getWidgetParent } from "sagas/selectors";
|
||||
|
||||
type Props = PropertyPaneControlConfig & {
|
||||
panel: IPanelProps;
|
||||
|
|
@ -71,6 +72,13 @@ const PropertyControl = memo((props: Props) => {
|
|||
isEqual,
|
||||
);
|
||||
|
||||
/**
|
||||
* get actual parent of widget
|
||||
* for button inside form, button's parent is form
|
||||
* for button on canvas, parent is main container
|
||||
*/
|
||||
const parentWidget = useSelector(getWidgetParent(widgetProperties.widgetId));
|
||||
|
||||
const enhancementSelector = getWidgetEnhancementSelector(
|
||||
widgetProperties.widgetId,
|
||||
);
|
||||
|
|
@ -410,7 +418,8 @@ const PropertyControl = memo((props: Props) => {
|
|||
|
||||
// Do not render the control if it needs to be hidden
|
||||
if (
|
||||
(props.hidden && props.hidden(widgetProperties, props.propertyName)) ||
|
||||
(props.hidden &&
|
||||
props.hidden(widgetProperties, props.propertyName, parentWidget)) ||
|
||||
props.invisible
|
||||
) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import { Collapse } from "@blueprintjs/core";
|
|||
import { useSelector } from "react-redux";
|
||||
import { getWidgetPropsForPropertyPane } from "selectors/propertyPaneSelectors";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { getWidgetParent } from "sagas/selectors";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
|
||||
const SectionWrapper = styled.div`
|
||||
position: relative;
|
||||
|
|
@ -53,7 +55,11 @@ type PropertySectionProps = {
|
|||
id: string;
|
||||
name: string;
|
||||
children?: ReactNode;
|
||||
hidden?: (props: any, propertyPath: string) => boolean;
|
||||
hidden?: (
|
||||
props: any,
|
||||
propertyPath: string,
|
||||
widgetParentProps?: WidgetProps,
|
||||
) => boolean;
|
||||
isDefaultOpen?: boolean;
|
||||
propertyPath?: string;
|
||||
};
|
||||
|
|
@ -69,8 +75,15 @@ export const PropertySection = memo((props: PropertySectionProps) => {
|
|||
const { isDefaultOpen = true } = props;
|
||||
const [isOpen, open] = useState(!!isDefaultOpen);
|
||||
const widgetProps: any = useSelector(getWidgetPropsForPropertyPane);
|
||||
/**
|
||||
* get actual parent of widget
|
||||
* for button inside form, button's parent is form
|
||||
* for button on canvas, parent is main container
|
||||
*/
|
||||
const parentWidget = useSelector(getWidgetParent(widgetProps.widgetId));
|
||||
|
||||
if (props.hidden) {
|
||||
if (props.hidden(widgetProps, props.propertyPath || "")) {
|
||||
if (props.hidden(widgetProps, props.propertyPath || "", parentWidget)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import {
|
|||
} from "sagas/ActionExecution/GetCurrentLocationSaga";
|
||||
import { requestModalConfirmationSaga } from "sagas/UtilSagas";
|
||||
import { ModalType } from "reducers/uiReducers/modalActionReducer";
|
||||
import { get, set, size } from "lodash";
|
||||
|
||||
export type TriggerMeta = {
|
||||
source?: TriggerSource;
|
||||
|
|
@ -67,7 +68,7 @@ export function* executeActionTriggers(
|
|||
triggerMeta: TriggerMeta,
|
||||
): any {
|
||||
// when called via a promise, a trigger can return some value to be used in .then
|
||||
let response: unknown[] = [];
|
||||
let response: unknown[] = [{ success: true }];
|
||||
switch (trigger.type) {
|
||||
case ActionTriggerType.RUN_PLUGIN_ACTION:
|
||||
response = yield call(
|
||||
|
|
@ -117,6 +118,8 @@ export function* executeActionTriggers(
|
|||
eventType,
|
||||
triggerMeta,
|
||||
);
|
||||
// response return only one object into array
|
||||
set(response, "0.success", true);
|
||||
break;
|
||||
|
||||
case ActionTriggerType.WATCH_CURRENT_LOCATION:
|
||||
|
|
@ -126,10 +129,14 @@ export function* executeActionTriggers(
|
|||
eventType,
|
||||
triggerMeta,
|
||||
);
|
||||
// response return only one object into array
|
||||
set(response, "0.success", true);
|
||||
break;
|
||||
|
||||
case ActionTriggerType.STOP_WATCHING_CURRENT_LOCATION:
|
||||
response = yield call(stopWatchCurrentLocation, eventType, triggerMeta);
|
||||
// response return only one object into array
|
||||
set(response, "0.success", true);
|
||||
break;
|
||||
case ActionTriggerType.CONFIRMATION_MODAL:
|
||||
const payloadInfo = {
|
||||
|
|
@ -149,7 +156,7 @@ export function* executeActionTriggers(
|
|||
return response;
|
||||
}
|
||||
|
||||
export function* executeAppAction(payload: ExecuteTriggerPayload) {
|
||||
export function* executeAppAction(payload: ExecuteTriggerPayload): any {
|
||||
const {
|
||||
callbackData,
|
||||
dynamicString,
|
||||
|
|
@ -163,7 +170,7 @@ export function* executeAppAction(payload: ExecuteTriggerPayload) {
|
|||
throw new Error("Executing undefined action");
|
||||
}
|
||||
|
||||
yield call(
|
||||
return yield call(
|
||||
evaluateAndExecuteDynamicTrigger,
|
||||
dynamicString,
|
||||
type,
|
||||
|
|
@ -181,9 +188,16 @@ function* initiateActionTriggerExecution(
|
|||
// it will be created again while execution
|
||||
AppsmithConsole.deleteError(`${source?.id}-${triggerPropertyName}`);
|
||||
try {
|
||||
yield call(executeAppAction, action.payload);
|
||||
const res: unknown[] = yield call(executeAppAction, action.payload);
|
||||
if (event.callback) {
|
||||
event.callback({ success: true });
|
||||
/**
|
||||
* result.success flag added to fire notification after successfully trigger
|
||||
* size of triggers checked for dependent action trigger i.e call success message after getting current location
|
||||
*/
|
||||
const success = !!(
|
||||
get(res, "result.success") || size(get(res, "triggers"))
|
||||
);
|
||||
event.callback({ success });
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof UncaughtPromiseError || e instanceof TriggerFailureError) {
|
||||
|
|
|
|||
|
|
@ -374,6 +374,10 @@ export default function* executePluginActionTriggerSaga(
|
|||
callbackData: [payload.body, params],
|
||||
...triggerMeta,
|
||||
});
|
||||
throw new PluginTriggerFailureError(
|
||||
createMessage(ERROR_ACTION_EXECUTE_FAIL, action.name),
|
||||
[payload.body, params],
|
||||
);
|
||||
} else {
|
||||
throw new PluginTriggerFailureError(
|
||||
createMessage(ERROR_PLUGIN_ACTION_EXECUTE, action.name),
|
||||
|
|
@ -402,9 +406,14 @@ export default function* executePluginActionTriggerSaga(
|
|||
callbackData: [payload.body, params],
|
||||
...triggerMeta,
|
||||
});
|
||||
return [{ success: true }];
|
||||
}
|
||||
}
|
||||
return [payload.body, params];
|
||||
// added success flag for successfull api execution and handle callback
|
||||
return [
|
||||
set((payload.body || {}) as Record<string, unknown>, "success", true),
|
||||
params,
|
||||
];
|
||||
}
|
||||
|
||||
function* runActionShortcutSaga() {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@ import {
|
|||
} from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import _ from "lodash";
|
||||
import { WidgetType } from "constants/WidgetConstants";
|
||||
import {
|
||||
WidgetType,
|
||||
MAIN_CONTAINER_WIDGET_ID,
|
||||
} from "constants/WidgetConstants";
|
||||
import { ActionData } from "reducers/entityReducers/actionsReducer";
|
||||
import { Page } from "@appsmith/constants/ReduxActionConstants";
|
||||
import { getActions, getPlugins } from "selectors/entitiesSelector";
|
||||
|
|
@ -175,3 +178,30 @@ export const getWidgetImmediateChildren = createSelector(
|
|||
return childrenIds;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* get actual parent of widget based on widgetId
|
||||
* for button inside form, button's parent is form
|
||||
* for button on canvas, parent is main container
|
||||
*/
|
||||
export const getWidgetParent = (widgetId: string) => {
|
||||
return createSelector(
|
||||
getWidgets,
|
||||
(canvasWidgets: CanvasWidgetsReduxState) => {
|
||||
let widget = canvasWidgets[widgetId];
|
||||
// While this widget has a parent
|
||||
while (widget?.parentId) {
|
||||
// Get parent widget props
|
||||
const parent = _.get(canvasWidgets, widget.parentId, undefined);
|
||||
// keep walking up the tree to find the parent untill parent exist or parent is the main container
|
||||
if (parent?.parentId && parent.parentId !== MAIN_CONTAINER_WIDGET_ID) {
|
||||
widget = canvasWidgets[widget.parentId];
|
||||
continue;
|
||||
} else {
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
return;
|
||||
},
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ export const CONFIG = {
|
|||
isDisabled: false,
|
||||
isVisible: true,
|
||||
isDefaultClickDisabled: true,
|
||||
disabledWhenInvalid: false,
|
||||
resetFormOnClick: false,
|
||||
recaptchaType: RecaptchaTypes.V3,
|
||||
version: 1,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import React from "react";
|
|||
import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget";
|
||||
import { WidgetType } from "constants/WidgetConstants";
|
||||
import ButtonComponent, { ButtonType } from "../component";
|
||||
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
||||
import {
|
||||
EventType,
|
||||
ExecutionResult,
|
||||
} from "constants/AppsmithActionConstants/ActionConstants";
|
||||
import { ValidationTypes } from "constants/WidgetValidation";
|
||||
import { DerivedPropertiesMap } from "utils/WidgetFactory";
|
||||
import { Alignment } from "@blueprintjs/core";
|
||||
|
|
@ -15,6 +18,7 @@ import {
|
|||
ButtonPlacementTypes,
|
||||
ButtonPlacement,
|
||||
} from "components/constants";
|
||||
import FormWidget from "widgets/FormWidget/widget";
|
||||
|
||||
class ButtonWidget extends BaseWidget<ButtonWidgetProps, ButtonWidgetState> {
|
||||
onButtonClickBound: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
|
|
@ -121,6 +125,39 @@ class ButtonWidget extends BaseWidget<ButtonWidgetProps, ButtonWidgetState> {
|
|||
},
|
||||
],
|
||||
},
|
||||
// TODO: refactor widgetParentProps implementation when we address #10659
|
||||
{
|
||||
sectionName: "Form options",
|
||||
hidden: (
|
||||
props: ButtonWidgetProps,
|
||||
propertyPath: string,
|
||||
widgetParentProps?: WidgetProps,
|
||||
) => widgetParentProps?.type !== FormWidget.getWidgetType(),
|
||||
children: [
|
||||
{
|
||||
helpText:
|
||||
"Disabled if the form is invalid, if this widget exists directly within a Form widget.",
|
||||
propertyName: "disabledWhenInvalid",
|
||||
label: "Disabled Invalid Forms",
|
||||
controlType: "SWITCH",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.BOOLEAN },
|
||||
},
|
||||
{
|
||||
helpText:
|
||||
"Resets the fields of the form, on click, if this widget exists directly within a Form widget.",
|
||||
propertyName: "resetFormOnClick",
|
||||
label: "Reset Form on Success",
|
||||
controlType: "SWITCH",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.BOOLEAN },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "Events",
|
||||
children: [
|
||||
|
|
@ -321,6 +358,8 @@ class ButtonWidget extends BaseWidget<ButtonWidgetProps, ButtonWidgetState> {
|
|||
callback: this.handleActionComplete,
|
||||
},
|
||||
});
|
||||
} else if (this.props.resetFormOnClick && this.props.onReset) {
|
||||
this.props.onReset();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -341,13 +380,22 @@ class ButtonWidget extends BaseWidget<ButtonWidgetProps, ButtonWidgetState> {
|
|||
}
|
||||
};
|
||||
|
||||
handleActionComplete = () => {
|
||||
handleActionComplete = (result: ExecutionResult) => {
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
});
|
||||
if (result.success) {
|
||||
if (this.props.resetFormOnClick && this.props.onReset)
|
||||
this.props.onReset();
|
||||
}
|
||||
};
|
||||
|
||||
getPageView() {
|
||||
const disabled =
|
||||
this.props.disabledWhenInvalid &&
|
||||
"isFormValid" in this.props &&
|
||||
!this.props.isFormValid;
|
||||
const isDisabled = this.props.isDisabled || disabled;
|
||||
return (
|
||||
<ButtonComponent
|
||||
borderRadius={this.props.borderRadius}
|
||||
|
|
@ -359,10 +407,10 @@ class ButtonWidget extends BaseWidget<ButtonWidgetProps, ButtonWidgetState> {
|
|||
handleRecaptchaV2Loading={this.handleRecaptchaV2Loading}
|
||||
iconAlign={this.props.iconAlign}
|
||||
iconName={this.props.iconName}
|
||||
isDisabled={this.props.isDisabled}
|
||||
isDisabled={isDisabled}
|
||||
isLoading={this.props.isLoading || this.state.isLoading}
|
||||
key={this.props.widgetId}
|
||||
onClick={!this.props.isDisabled ? this.onButtonClickBound : undefined}
|
||||
onClick={isDisabled ? undefined : this.onButtonClickBound}
|
||||
placement={this.props.placement}
|
||||
recaptchaType={this.props.recaptchaType}
|
||||
text={this.props.text}
|
||||
|
|
@ -394,6 +442,8 @@ export interface ButtonWidgetProps extends WidgetProps {
|
|||
iconName?: IconName;
|
||||
iconAlign?: Alignment;
|
||||
placement?: ButtonPlacement;
|
||||
disabledWhenInvalid?: boolean;
|
||||
resetFormOnClick?: boolean;
|
||||
}
|
||||
|
||||
interface ButtonWidgetState extends WidgetState {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ export const CONFIG = {
|
|||
name: "FormButton",
|
||||
iconSVG: IconSVG,
|
||||
hideCard: true,
|
||||
isDeprecated: true,
|
||||
replacement: "BUTTON_WIDGET",
|
||||
needsMeta: true,
|
||||
defaults: {
|
||||
rows: 4,
|
||||
|
|
|
|||
|
|
@ -47,28 +47,6 @@ class FormButtonWidget extends ButtonWidget {
|
|||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.TEXT },
|
||||
},
|
||||
{
|
||||
helpText:
|
||||
"Disables the button when the parent form has a required widget that is not filled",
|
||||
propertyName: "disabledWhenInvalid",
|
||||
label: "Disabled Invalid Forms",
|
||||
controlType: "SWITCH",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.BOOLEAN },
|
||||
},
|
||||
{
|
||||
helpText:
|
||||
"Resets the fields within the parent form when the click action succeeds",
|
||||
propertyName: "resetFormOnClick",
|
||||
label: "Reset Form on Success",
|
||||
controlType: "SWITCH",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.BOOLEAN },
|
||||
},
|
||||
{
|
||||
propertyName: "isVisible",
|
||||
label: "Visible",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export const CONFIG = {
|
|||
},
|
||||
},
|
||||
{
|
||||
type: "FORM_BUTTON_WIDGET",
|
||||
type: "BUTTON_WIDGET",
|
||||
size: {
|
||||
rows: 4,
|
||||
cols: 16,
|
||||
|
|
@ -63,7 +63,7 @@ export const CONFIG = {
|
|||
},
|
||||
},
|
||||
{
|
||||
type: "FORM_BUTTON_WIDGET",
|
||||
type: "BUTTON_WIDGET",
|
||||
size: {
|
||||
rows: 4,
|
||||
cols: 16,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user