Skip to content

[Fix]: backward compatibility for button, link and links column types #1769

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 14, 2025
Prev Previous commit
Next Next commit
[Fix]: Add backward compatibility for button, link and links
  • Loading branch information
iamfaran committed Jun 13, 2025
commit a87e00b781154cb486bdcec4619ec8af8d036adc
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ const StyledLink = styled.a<{ $disabled: boolean }>`
${(props) => props.$disabled && disableCss};
`;

// Memoized link component
// Updated link component to handle both legacy and new event handlers
export const ColumnLink = React.memo(({ disabled, label, onClick, onEvent }: { disabled: boolean; label: string; onClick?: () => void; onEvent?: (eventName: string) => void }) => {
const handleClick = useCallback(() => {
if (disabled) return;
onClick?.();
// onEvent?.("click");
if (!disabled) {
// Trigger legacy onClick action for backward compatibility
onClick?.();
// Trigger new event handlers
onEvent?.("click");
}
}, [disabled, onClick, onEvent]);

return (
Expand Down Expand Up @@ -110,7 +113,7 @@ export const LinkComp = (function () {
childrenMap,
(props, dispatch) => {
const value = props.changeValue ?? getBaseValue(props, dispatch);
return <ColumnLink disabled={props.disabled} label={value} onClick={props.onClick} />;
return <ColumnLink disabled={props.disabled} label={value} onClick={props.onClick} onEvent={props.onEvent} />;
},
(nodeValue) => nodeValue.text.value,
getBaseValue
Expand All @@ -122,20 +125,27 @@ export const LinkComp = (function () {
onChangeEnd={props.onChangeEnd}
/>
))
.setPropertyViewFn((children) => (
<>
{children.text.propertyView({
label: trans("table.columnValue"),
tooltip: ColumnValueTooltip,
})}
{disabledPropertyView(children)}
{/* {children.onEvent.propertyView()} */}
{children.onClick.propertyView({
label: trans("table.action"),
placement: "table",
})}
</>
))
.setPropertyViewFn((children) => {
// Check if there's a legacy action configured
const hasLegacyAction = children.onClick.getView() &&
typeof children.onClick.getView() === 'function' &&
children.onClick.displayName() !== trans("eventHandler.incomplete");

return (
<>
{children.text.propertyView({
label: trans("table.columnValue"),
tooltip: ColumnValueTooltip,
})}
{disabledPropertyView(children)}
{children.onEvent.propertyView()}
{hasLegacyAction && children.onClick.propertyView({
label: trans("table.action"),
placement: "table",
})}
</>
);
})
.setStylePropertyViewFn((children) => (
<>
{children.style.getPropertyView()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,65 +40,72 @@ const MenuWrapper = styled.div`

const LinksEventOptions = [clickEvent] as const;

// Memoized menu item component
const MenuItem = React.memo(({ option, index, onMainEvent }: { option: any; index: number; onMainEvent?: (eventName: string) => void }) => {
const handleClick = useCallback(() => {
if (!option.disabled) {
// Trigger legacy onClick action for backward compatibility
if (option.onClick) {
option.onClick();
}
// Trigger individual item event handlers
if (option.onEvent) {
option.onEvent("click");
}
// Trigger the main column's event handler
if (onMainEvent) {
onMainEvent("click");
}
}
}, [option.disabled, option.onClick, option.onEvent, onMainEvent]);

return (
<MenuLinkWrapper>
<ColumnLink
disabled={option.disabled}
label={option.label}
onClick={handleClick}
/>
</MenuLinkWrapper>
);
});

MenuItem.displayName = 'MenuItem';

// Update OptionItem to include event handlers
const OptionItem = new MultiCompBuilder(
{
label: StringControl,
onClick: ActionSelectorControlInContext,
onEvent: eventHandlerControl(LinksEventOptions),
hidden: BoolCodeControl,
disabled: BoolCodeControl,
onEvent: eventHandlerControl(LinksEventOptions),
},
(props) => {
return props;
}
)
.setPropertyViewFn((children) => {
// Check if there's a legacy action configured for this individual item
const hasLegacyAction = children.onClick.getView() &&
typeof children.onClick.getView() === 'function' &&
children.onClick.displayName() !== trans("eventHandler.incomplete");

return (
<>
{children.label.propertyView({ label: trans("label") })}
{children.onClick.propertyView({
{hasLegacyAction && children.onClick.propertyView({
label: trans("table.action"),
placement: "table",
})}
{hiddenPropertyView(children)}
{disabledPropertyView(children)}
{/* {children.onEvent.propertyView()} */}
{children.onEvent.propertyView()}
</>
);
})
.build();

// Memoized menu item component
const MenuItem = React.memo(({ option, index, onMainEvent }: { option: any; index: number; onMainEvent?: (eventName: string) => void }) => {
const handleClick = useCallback(() => {
if (!option.disabled) {
if (option.onClick) {
option.onClick();
}
// if (option.onEvent) {
// option.onEvent("click");
// }
// Trigger the main component's event handler
if (onMainEvent) {
onMainEvent("click");
}
}
}, [option.disabled, option.onClick, option.onEvent, onMainEvent]);

return (
<MenuLinkWrapper>
<ColumnLink
disabled={option.disabled}
label={option.label}
onEvent={handleClick}
/>
</MenuLinkWrapper>
);
});

MenuItem.displayName = 'MenuItem';

// Memoized menu component
const LinksMenu = React.memo(({ options, onEvent }: { options: any[]; onEvent?: (eventName: string) => void }) => {
const mountedRef = useRef(true);
Expand Down Expand Up @@ -134,7 +141,7 @@ export const ColumnLinksComp = (function () {
options: manualOptionsControl(OptionItem, {
initOptions: [{ label: trans("table.option1") }],
}),
onEvent: eventHandlerControl(LinksEventOptions),
onEvent: eventHandlerControl(LinksEventOptions), // Main column level event handlers
};
return new ColumnTypeCompBuilder(
childrenMap,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import React, { useCallback, useEffect, useMemo } from "react";
import { CSSProperties } from "react";
import { RecordConstructorToComp } from "lowcoder-core";
import { ToViewReturn } from "@lowcoder-ee/comps/generators/multi";
import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl";

export const ColumnValueTooltip = trans("table.columnValueTooltip");

Expand All @@ -31,10 +32,13 @@ export const ButtonTypeOptions = [
},
] as const;

const ButtonEventOptions = [clickEvent] as const;

const childrenMap = {
text: StringControl,
buttonType: dropdownControl(ButtonTypeOptions, "primary"),
onClick: ActionSelectorControlInContext,
onEvent: eventHandlerControl(ButtonEventOptions),
loading: BoolCodeControl,
disabled: BoolCodeControl,
prefixIcon: IconControl,
Expand All @@ -49,8 +53,11 @@ const ButtonStyled = React.memo(({ props }: { props: ToViewReturn<RecordConstruc
const iconOnly = !hasText && (hasPrefixIcon || hasSuffixIcon);

const handleClick = useCallback((e: React.MouseEvent) => {
// Trigger legacy onClick action for backward compatibility
props.onClick?.();
}, [props.onClick]);
// Trigger new event handlers
props.onEvent?.("click");
}, [props.onClick, props.onEvent]);

const buttonStyle = useMemo(() => ({
margin: 0,
Expand Down Expand Up @@ -82,29 +89,37 @@ export const ButtonComp = (function () {
(props) => <ButtonStyled props={props} />,
(nodeValue) => nodeValue.text.value
)
.setPropertyViewFn((children) => (
<>
{children.text.propertyView({
label: trans("table.columnValue"),
tooltip: ColumnValueTooltip,
})}
{children.prefixIcon.propertyView({
label: trans("button.prefixIcon"),
})}
{children.suffixIcon.propertyView({
label: trans("button.suffixIcon"),
})}
{children.buttonType.propertyView({
label: trans("table.type"),
radioButton: true,
})}
{loadingPropertyView(children)}
{disabledPropertyView(children)}
{children.onClick.propertyView({
label: trans("table.action"),
placement: "table",
})}
</>
))
.setPropertyViewFn((children) => {
// Check if there's a legacy action configured
const hasLegacyAction = children.onClick.getView() &&
typeof children.onClick.getView() === 'function' &&
children.onClick.displayName() !== trans("eventHandler.incomplete");

return (
<>
{children.text.propertyView({
label: trans("table.columnValue"),
tooltip: ColumnValueTooltip,
})}
{children.prefixIcon.propertyView({
label: trans("button.prefixIcon"),
})}
{children.suffixIcon.propertyView({
label: trans("button.suffixIcon"),
})}
{children.buttonType.propertyView({
label: trans("table.type"),
radioButton: true,
})}
{loadingPropertyView(children)}
{disabledPropertyView(children)}
{children.onEvent.propertyView()}
{hasLegacyAction && children.onClick.propertyView({
label: trans("table.action"),
placement: "table",
})}
</>
);
})
.build();
})();