feat: Dynamic Menu Items - Table Widget (#18945)
## Description This PR allows adding menu items from a Source for a Menu Button Column inside the Table Widget.
This commit is contained in:
parent
fc9d7486fa
commit
5c3c5337bb
|
|
@ -18,6 +18,8 @@ import {
|
|||
stringToJS,
|
||||
} from "components/editorComponents/ActionCreator/utils";
|
||||
import { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator";
|
||||
import { ColumnProperties } from "widgets/TableWidgetV2/component/Constants";
|
||||
import { getUniqueKeysFromSourceData } from "widgets/MenuButtonWidget/widget/helper";
|
||||
|
||||
const PromptMessage = styled.span`
|
||||
line-height: 17px;
|
||||
|
|
@ -93,18 +95,34 @@ class MenuButtonDynamicItemsControl extends BaseControl<
|
|||
label,
|
||||
propertyValue,
|
||||
theme,
|
||||
widgetProperties,
|
||||
} = this.props;
|
||||
const menuButtonId = this.props.widgetProperties.widgetName;
|
||||
const widgetName = widgetProperties.widgetName;
|
||||
const widgetType = widgetProperties.type;
|
||||
const value =
|
||||
propertyValue && isDynamicValue(propertyValue)
|
||||
? MenuButtonDynamicItemsControl.getInputComputedValue(
|
||||
propertyValue,
|
||||
menuButtonId,
|
||||
widgetName,
|
||||
widgetType,
|
||||
widgetProperties.primaryColumns,
|
||||
)
|
||||
: propertyValue
|
||||
? propertyValue
|
||||
: defaultValue;
|
||||
const keys = this.props.widgetProperties.sourceDataKeys || [];
|
||||
let sourceData;
|
||||
|
||||
if (widgetType === "TABLE_WIDGET_V2") {
|
||||
sourceData =
|
||||
widgetProperties?.__evaluation__?.evaluatedValues?.primaryColumns?.[
|
||||
`${Object.keys(widgetProperties.primaryColumns)[0]}`
|
||||
]?.sourceData;
|
||||
} else if (widgetType === "MENU_BUTTON_WIDGET") {
|
||||
sourceData =
|
||||
widgetProperties?.__evaluation__?.evaluatedValues?.sourceData;
|
||||
}
|
||||
|
||||
const keys = getUniqueKeysFromSourceData(sourceData);
|
||||
const currentItem: { [key: string]: any } = {};
|
||||
|
||||
Object.values(keys).forEach((key) => {
|
||||
|
|
@ -115,6 +133,7 @@ class MenuButtonDynamicItemsControl extends BaseControl<
|
|||
if (value && !propertyValue) {
|
||||
this.onTextChange(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<InputText
|
||||
additionalDynamicData={{
|
||||
|
|
@ -131,30 +150,61 @@ class MenuButtonDynamicItemsControl extends BaseControl<
|
|||
);
|
||||
}
|
||||
|
||||
static getBindingPrefix = (menuButtonId: string) => {
|
||||
return `{{${menuButtonId}.sourceData.map((currentItem, currentIndex) => ( `;
|
||||
static getBindingPrefix = (
|
||||
widgetName: string,
|
||||
widgetType?: string,
|
||||
primaryColumns?: Record<string, ColumnProperties>,
|
||||
) => {
|
||||
if (widgetType === "TABLE_WIDGET_V2" && primaryColumns) {
|
||||
const columnName = Object.keys(primaryColumns)?.[0];
|
||||
|
||||
return `{{${widgetName}.processedTableData.map((currentRow, currentRowIndex) => {
|
||||
let primaryColumnData = [];
|
||||
|
||||
if (${widgetName}.primaryColumns.${columnName}.sourceData[currentRowIndex].length) {
|
||||
primaryColumnData = ${widgetName}.primaryColumns.${columnName}.sourceData[currentRowIndex];
|
||||
} else if (${widgetName}.primaryColumns.${columnName}.sourceData.length) {
|
||||
primaryColumnData = ${widgetName}.primaryColumns.${columnName}.sourceData;
|
||||
}
|
||||
|
||||
return primaryColumnData.map((currentItem, currentIndex) => `;
|
||||
} else {
|
||||
return `{{${widgetName}.sourceData.map((currentItem, currentIndex) => ( `;
|
||||
}
|
||||
};
|
||||
|
||||
static bindingSuffix = `))}}`;
|
||||
static getBindingSuffix = (widgetType?: string) =>
|
||||
widgetType === "TABLE_WIDGET_V2" ? `);});}}` : `))}}`;
|
||||
|
||||
static getInputComputedValue = (
|
||||
propertyValue: string,
|
||||
menuButtonId: string,
|
||||
widgetName: string,
|
||||
widgetType?: string,
|
||||
primaryColumns?: Record<string, ColumnProperties>,
|
||||
) => {
|
||||
if (!propertyValue.includes(this.getBindingPrefix(menuButtonId))) {
|
||||
if (
|
||||
!propertyValue.includes(
|
||||
this.getBindingPrefix(widgetName, widgetType, primaryColumns),
|
||||
)
|
||||
) {
|
||||
return propertyValue;
|
||||
}
|
||||
|
||||
const value = `${propertyValue.substring(
|
||||
this.getBindingPrefix(menuButtonId).length,
|
||||
propertyValue.length - this.bindingSuffix.length,
|
||||
this.getBindingPrefix(widgetName, widgetType, primaryColumns).length,
|
||||
propertyValue.length - this.getBindingSuffix(widgetType).length,
|
||||
)}`;
|
||||
const stringValue = JSToString(value);
|
||||
|
||||
return stringValue;
|
||||
};
|
||||
|
||||
getComputedValue = (value: string, menuButtonId: string) => {
|
||||
getComputedValue = (
|
||||
value: string,
|
||||
widgetName: string,
|
||||
widgetType?: string,
|
||||
primaryColumns?: Record<string, ColumnProperties>,
|
||||
) => {
|
||||
if (!isDynamicValue(value)) {
|
||||
return value;
|
||||
}
|
||||
|
|
@ -166,8 +216,12 @@ class MenuButtonDynamicItemsControl extends BaseControl<
|
|||
}
|
||||
|
||||
return `${MenuButtonDynamicItemsControl.getBindingPrefix(
|
||||
menuButtonId,
|
||||
)}${stringToEvaluate}${MenuButtonDynamicItemsControl.bindingSuffix}`;
|
||||
widgetName,
|
||||
widgetType,
|
||||
primaryColumns,
|
||||
)}${stringToEvaluate}${MenuButtonDynamicItemsControl.getBindingSuffix(
|
||||
widgetType,
|
||||
)}`;
|
||||
};
|
||||
|
||||
onTextChange = (event: React.ChangeEvent<HTMLTextAreaElement> | string) => {
|
||||
|
|
@ -181,6 +235,8 @@ class MenuButtonDynamicItemsControl extends BaseControl<
|
|||
const output = this.getComputedValue(
|
||||
value,
|
||||
this.props.widgetProperties.widgetName,
|
||||
this.props.widgetProperties.type,
|
||||
this.props.widgetProperties.primaryColumns,
|
||||
);
|
||||
|
||||
this.updateProperty(this.props.propertyName, output);
|
||||
|
|
|
|||
|
|
@ -177,11 +177,13 @@ class ComputeTablePropertyControlV2 extends BaseControl<
|
|||
|
||||
onTextChange = (event: React.ChangeEvent<HTMLTextAreaElement> | string) => {
|
||||
let value = "";
|
||||
|
||||
if (typeof event !== "string") {
|
||||
value = event.target?.value;
|
||||
} else {
|
||||
value = event;
|
||||
}
|
||||
|
||||
if (isString(value)) {
|
||||
const output = this.getComputedValue(
|
||||
value,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ export const layoutConfigurations: LayoutConfigurations = {
|
|||
FLUID: { minWidth: -1, maxWidth: -1 },
|
||||
};
|
||||
|
||||
export const LATEST_PAGE_VERSION = 74;
|
||||
export const LATEST_PAGE_VERSION = 75;
|
||||
|
||||
export const GridDefaults = {
|
||||
DEFAULT_CELL_SIZE: 1,
|
||||
|
|
|
|||
|
|
@ -718,6 +718,15 @@ const migrations: Migration[] = [
|
|||
],
|
||||
version: 73,
|
||||
},
|
||||
{
|
||||
functionLookup: [
|
||||
{
|
||||
moduleObj: tableMigrations,
|
||||
functionName: "migrateMenuButtonDynamicItemsInsideTableWidget",
|
||||
},
|
||||
],
|
||||
version: 74,
|
||||
},
|
||||
];
|
||||
|
||||
const mockFnObj: Record<number, any> = {};
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {
|
|||
migrateTableWidgetIconButtonVariant,
|
||||
migrateTableWidgetV2Validation,
|
||||
migrateTableWidgetV2ValidationBinding,
|
||||
migrateMenuButtonDynamicItemsInsideTableWidget,
|
||||
migrateTableWidgetV2SelectOption,
|
||||
} from "./migrations/TableWidget";
|
||||
import {
|
||||
|
|
@ -1155,6 +1156,11 @@ export const transformDSL = (currentDSL: DSLWidget, newPage = false) => {
|
|||
|
||||
if (currentDSL.version === 73) {
|
||||
currentDSL = migrateInputWidgetShowStepArrows(currentDSL);
|
||||
currentDSL.version = 74;
|
||||
}
|
||||
|
||||
if (currentDSL.version === 74) {
|
||||
currentDSL = migrateMenuButtonDynamicItemsInsideTableWidget(currentDSL);
|
||||
currentDSL.version = LATEST_PAGE_VERSION;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -670,3 +670,25 @@ export const migrateTableWidgetV2SelectOption = (currentDSL: DSLWidget) => {
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const migrateMenuButtonDynamicItemsInsideTableWidget = (
|
||||
currentDSL: DSLWidget,
|
||||
) => {
|
||||
return traverseDSLAndMigrate(currentDSL, (widget: WidgetProps) => {
|
||||
if (widget.type === "TABLE_WIDGET_V2") {
|
||||
const primaryColumns = widget.primaryColumns;
|
||||
|
||||
if (primaryColumns) {
|
||||
for (const column in primaryColumns) {
|
||||
if (
|
||||
primaryColumns.hasOwnProperty(column) &&
|
||||
primaryColumns[column].columnType === "menuButton" &&
|
||||
!primaryColumns[column].menuItemsSource
|
||||
) {
|
||||
primaryColumns[column].menuItemsSource = "STATIC";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,12 +34,14 @@ export interface ConfigureMenuItems {
|
|||
config: MenuItem;
|
||||
}
|
||||
|
||||
export type MenuItems = Record<string, MenuItem>;
|
||||
|
||||
export interface MenuButtonWidgetProps extends WidgetProps {
|
||||
label?: string;
|
||||
isDisabled?: boolean;
|
||||
isVisible?: boolean;
|
||||
isCompact?: boolean;
|
||||
menuItems: Record<string, MenuItem>;
|
||||
menuItems: MenuItems;
|
||||
getVisibleItems: () => Array<MenuItem>;
|
||||
menuVariant?: ButtonVariant;
|
||||
menuColor?: string;
|
||||
|
|
@ -51,7 +53,6 @@ export interface MenuButtonWidgetProps extends WidgetProps {
|
|||
menuItemsSource: MenuItemsSource;
|
||||
configureMenuItems: ConfigureMenuItems;
|
||||
sourceData?: Array<Record<string, unknown>>;
|
||||
sourceDataKeys?: Array<string>;
|
||||
}
|
||||
|
||||
export interface MenuButtonComponentProps {
|
||||
|
|
@ -59,7 +60,7 @@ export interface MenuButtonComponentProps {
|
|||
isDisabled?: boolean;
|
||||
isVisible?: boolean;
|
||||
isCompact?: boolean;
|
||||
menuItems: Record<string, MenuItem>;
|
||||
menuItems: MenuItems;
|
||||
getVisibleItems: () => Array<MenuItem>;
|
||||
menuVariant?: ButtonVariant;
|
||||
menuColor?: string;
|
||||
|
|
@ -77,11 +78,10 @@ export interface MenuButtonComponentProps {
|
|||
menuItemsSource: MenuItemsSource;
|
||||
configureMenuItems: ConfigureMenuItems;
|
||||
sourceData?: Array<Record<string, unknown>>;
|
||||
sourceDataKeys?: Array<string>;
|
||||
}
|
||||
|
||||
export interface PopoverContentProps {
|
||||
menuItems: Record<string, MenuItem>;
|
||||
menuItems: MenuItems;
|
||||
getVisibleItems: () => Array<MenuItem>;
|
||||
onItemClicked: (onClick: string | undefined, index: number) => void;
|
||||
isCompact?: boolean;
|
||||
|
|
@ -90,7 +90,6 @@ export interface PopoverContentProps {
|
|||
menuItemsSource: MenuItemsSource;
|
||||
configureMenuItems: ConfigureMenuItems;
|
||||
sourceData?: Array<Record<string, unknown>>;
|
||||
sourceDataKeys?: Array<string>;
|
||||
}
|
||||
|
||||
export const ICON_NAMES = Object.keys(IconNames).map(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { ValidationConfig } from "constants/PropertyControlConstants";
|
||||
import { ValidationResponse } from "constants/WidgetValidation";
|
||||
import { MenuButtonWidgetProps } from "./constants";
|
||||
|
||||
|
|
@ -43,3 +44,253 @@ export function sourceDataArrayValidation(
|
|||
return invalidResponse;
|
||||
}
|
||||
}
|
||||
|
||||
export function textForEachRowValidation(
|
||||
value: unknown,
|
||||
props: MenuButtonWidgetProps,
|
||||
_: any,
|
||||
): ValidationResponse {
|
||||
const generateResponseAndReturn = (isValid = false, message = "") => {
|
||||
return {
|
||||
isValid,
|
||||
parsed: isValid ? value : [],
|
||||
messages: [message],
|
||||
};
|
||||
};
|
||||
|
||||
const DEFAULT_MESSAGE =
|
||||
"The evaluated value should be either a string or a number.";
|
||||
|
||||
if (
|
||||
_.isString(value) ||
|
||||
_.isNumber(value) ||
|
||||
Array.isArray(value) ||
|
||||
value === undefined
|
||||
) {
|
||||
if (Array.isArray(value)) {
|
||||
const isValid = value.every((item) => {
|
||||
if (_.isString(item) || _.isNumber(item) || item === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
return item.every(
|
||||
(subItem) =>
|
||||
_.isString(subItem) ||
|
||||
_.isNumber(subItem) ||
|
||||
subItem === undefined,
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return isValid
|
||||
? generateResponseAndReturn(true)
|
||||
: generateResponseAndReturn(false, DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
return generateResponseAndReturn(true);
|
||||
}
|
||||
|
||||
return generateResponseAndReturn(false, DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
export function booleanForEachRowValidation(
|
||||
value: unknown,
|
||||
): ValidationResponse {
|
||||
const generateResponseAndReturn = (isValid = false, message = "") => {
|
||||
return {
|
||||
isValid,
|
||||
parsed: isValid ? value : true,
|
||||
messages: [message],
|
||||
};
|
||||
};
|
||||
|
||||
const isBoolean = (value: unknown) => {
|
||||
const isABoolean = value === true || value === false;
|
||||
const isStringTrueFalse = value === "true" || value === "false";
|
||||
|
||||
return isABoolean || isStringTrueFalse || value === undefined;
|
||||
};
|
||||
|
||||
const DEFAULT_MESSAGE = "The evaluated value should be a boolean.";
|
||||
|
||||
if (isBoolean(value)) {
|
||||
return generateResponseAndReturn(true);
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const isValid = value.every((item) => {
|
||||
if (isBoolean(item)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
return item.every((subItem) => isBoolean(subItem));
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return isValid
|
||||
? generateResponseAndReturn(true)
|
||||
: generateResponseAndReturn(false, DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
return generateResponseAndReturn(false, DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
export function iconNamesForEachRowValidation(
|
||||
value: unknown,
|
||||
props: MenuButtonWidgetProps,
|
||||
_: any,
|
||||
moment: any,
|
||||
propertyPath: string,
|
||||
config: ValidationConfig,
|
||||
): ValidationResponse {
|
||||
const generateResponseAndReturn = (isValid = false, message = "") => {
|
||||
return {
|
||||
isValid,
|
||||
parsed: isValid ? value : true,
|
||||
messages: [message],
|
||||
};
|
||||
};
|
||||
|
||||
const DEFAULT_MESSAGE =
|
||||
"The evaluated value should either be an icon name, undefined, null, or an empty string. We currently use the icons from the Blueprint library. You can see the list of icons at https://blueprintjs.com/docs/#icons";
|
||||
|
||||
const isIconName = (value: unknown) => {
|
||||
return (
|
||||
config?.params?.allowedValues?.includes(value as string) ||
|
||||
value === undefined ||
|
||||
value === null ||
|
||||
value === ""
|
||||
);
|
||||
};
|
||||
|
||||
if (isIconName(value)) {
|
||||
return generateResponseAndReturn(true);
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const isValid = value.every((item) => {
|
||||
if (isIconName(item)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
return item.every((subItem) => isIconName(subItem));
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return isValid
|
||||
? generateResponseAndReturn(true)
|
||||
: generateResponseAndReturn(false, DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
return generateResponseAndReturn(false, DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
export function iconPositionForEachRowValidation(
|
||||
value: unknown,
|
||||
props: MenuButtonWidgetProps,
|
||||
_: any,
|
||||
moment: any,
|
||||
propertyPath: string,
|
||||
config: ValidationConfig,
|
||||
): ValidationResponse {
|
||||
const generateResponseAndReturn = (isValid = false, message = "") => {
|
||||
return {
|
||||
isValid,
|
||||
parsed: isValid ? value : true,
|
||||
messages: [message],
|
||||
};
|
||||
};
|
||||
|
||||
const DEFAULT_MESSAGE = `The evaluated value should be one of the allowed values => ${config?.params?.allowedValues?.join(
|
||||
", ",
|
||||
)}, undefined, null, or an empty string`;
|
||||
|
||||
const isIconPosition = (value: unknown) => {
|
||||
return (
|
||||
config?.params?.allowedValues?.includes(value as string) ||
|
||||
value === undefined ||
|
||||
value === null ||
|
||||
value === ""
|
||||
);
|
||||
};
|
||||
|
||||
if (isIconPosition(value)) {
|
||||
return generateResponseAndReturn(true);
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const isValid = value.every((item) => {
|
||||
if (isIconPosition(item)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
return item.every((subItem) => isIconPosition(subItem));
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return isValid
|
||||
? generateResponseAndReturn(true)
|
||||
: generateResponseAndReturn(false, DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
return generateResponseAndReturn(false, DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
export function colorForEachRowValidation(
|
||||
value: unknown,
|
||||
props: MenuButtonWidgetProps,
|
||||
_: any,
|
||||
moment: any,
|
||||
propertyPath: string,
|
||||
config: ValidationConfig,
|
||||
): ValidationResponse {
|
||||
const generateResponseAndReturn = (isValid = false, message = "") => {
|
||||
return {
|
||||
isValid,
|
||||
parsed: isValid ? value : true,
|
||||
messages: [message],
|
||||
};
|
||||
};
|
||||
|
||||
const DEFAULT_MESSAGE = `The evaluated value should match ${config?.params?.regex}`;
|
||||
|
||||
const isColor = (value: unknown) => {
|
||||
return config?.params?.regex?.test(value as string);
|
||||
};
|
||||
|
||||
if (isColor(value)) {
|
||||
return generateResponseAndReturn(true);
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const isValid = value.every((item) => {
|
||||
if (isColor(item)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
return item.every((subItem) => isColor(subItem));
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return isValid
|
||||
? generateResponseAndReturn(true)
|
||||
: generateResponseAndReturn(false, DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
return generateResponseAndReturn(false, DEFAULT_MESSAGE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,29 @@
|
|||
import { getSourceDataKeysForEventAutocomplete } from "./helper";
|
||||
import { getKeysFromSourceDataForEventAutocomplete } from "./helper";
|
||||
|
||||
describe("getSourceDataKeysForEventAutocomplete", () => {
|
||||
it("Should test with valid values", () => {
|
||||
const mockProps = {
|
||||
sourceDataKeys: ["step", "task", "status", "action"],
|
||||
menuItemsSource: "DYANMIC",
|
||||
};
|
||||
describe("getKeysFromSourceDataForEventAutocomplete", () => {
|
||||
it("Should test with valid values - array of objects", () => {
|
||||
const mockProps = [
|
||||
{
|
||||
step: "#1",
|
||||
task: "Drop a table",
|
||||
status: "✅",
|
||||
action: "",
|
||||
},
|
||||
{
|
||||
step: "#2",
|
||||
task: "Create a query fetch_users with the Mock DB",
|
||||
status: "--",
|
||||
action: "",
|
||||
},
|
||||
{
|
||||
step: "#3",
|
||||
task: "Bind the query using => fetch_users.data",
|
||||
status: "--",
|
||||
action: "",
|
||||
},
|
||||
];
|
||||
|
||||
const result = getSourceDataKeysForEventAutocomplete(mockProps as any);
|
||||
const result = getKeysFromSourceDataForEventAutocomplete(mockProps as any);
|
||||
const expected = {
|
||||
currentItem: {
|
||||
step: "",
|
||||
|
|
@ -19,25 +35,169 @@ describe("getSourceDataKeysForEventAutocomplete", () => {
|
|||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it("Should test with Static menuItemSource", () => {
|
||||
const mockProps = {
|
||||
sourceDataKeys: [],
|
||||
menuItemsSource: "STATIC",
|
||||
};
|
||||
it("Should test with valid values - array of arrays of objects", () => {
|
||||
const mockProps = [
|
||||
[
|
||||
{
|
||||
gender: "male",
|
||||
name: "#1 Victor",
|
||||
email: "victor.garrett@example.com",
|
||||
phone: "011-800-3906",
|
||||
id: "6125683T",
|
||||
nat: "IE",
|
||||
},
|
||||
{
|
||||
gender: "male",
|
||||
name: "#1 Tobias",
|
||||
email: "tobias.hansen@example.com",
|
||||
phone: "84467012",
|
||||
id: "200247-8744",
|
||||
nat: "DK",
|
||||
},
|
||||
{
|
||||
gender: "female",
|
||||
name: "#1 Jane",
|
||||
email: "jane.coleman@example.com",
|
||||
phone: "(679) 516-8766",
|
||||
id: "098-73-7712",
|
||||
nat: "US",
|
||||
},
|
||||
{
|
||||
gender: "female",
|
||||
name: "#1 Yaromira",
|
||||
email: "yaromira.manuylenko@example.com",
|
||||
phone: "(099) B82-8594",
|
||||
id: null,
|
||||
nat: "UA",
|
||||
},
|
||||
{
|
||||
gender: "male",
|
||||
name: "#1 Andre",
|
||||
email: "andre.ortiz@example.com",
|
||||
phone: "08-3115-5776",
|
||||
id: "876838842",
|
||||
nat: "AU",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
gender: "male",
|
||||
name: "#2 Victor",
|
||||
email: "victor.garrett@example.com",
|
||||
phone: "011-800-3906",
|
||||
id: "6125683T",
|
||||
nat: "IE",
|
||||
},
|
||||
{
|
||||
gender: "male",
|
||||
name: "#2 Tobias",
|
||||
email: "tobias.hansen@example.com",
|
||||
phone: "84467012",
|
||||
id: "200247-8744",
|
||||
nat: "DK",
|
||||
},
|
||||
{
|
||||
gender: "female",
|
||||
name: "#2 Jane",
|
||||
email: "jane.coleman@example.com",
|
||||
phone: "(679) 516-8766",
|
||||
id: "098-73-7712",
|
||||
nat: "US",
|
||||
},
|
||||
{
|
||||
gender: "female",
|
||||
name: "#2 Yaromira",
|
||||
email: "yaromira.manuylenko@example.com",
|
||||
phone: "(099) B82-8594",
|
||||
id: null,
|
||||
nat: "UA",
|
||||
},
|
||||
{
|
||||
gender: "male",
|
||||
name: "#2 Andre",
|
||||
email: "andre.ortiz@example.com",
|
||||
phone: "08-3115-5776",
|
||||
id: "876838842",
|
||||
nat: "AU",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
gender: "male",
|
||||
name: "#3 Victor",
|
||||
email: "victor.garrett@example.com",
|
||||
phone: "011-800-3906",
|
||||
id: "6125683T",
|
||||
nat: "IE",
|
||||
},
|
||||
{
|
||||
gender: "male",
|
||||
name: "#3 Tobias",
|
||||
email: "tobias.hansen@example.com",
|
||||
phone: "84467012",
|
||||
id: "200247-8744",
|
||||
nat: "DK",
|
||||
},
|
||||
{
|
||||
gender: "female",
|
||||
name: "#3 Jane",
|
||||
email: "jane.coleman@example.com",
|
||||
phone: "(679) 516-8766",
|
||||
id: "098-73-7712",
|
||||
nat: "US",
|
||||
},
|
||||
{
|
||||
gender: "female",
|
||||
name: "#3 Yaromira",
|
||||
email: "yaromira.manuylenko@example.com",
|
||||
phone: "(099) B82-8594",
|
||||
id: null,
|
||||
nat: "UA",
|
||||
},
|
||||
{
|
||||
gender: "male",
|
||||
name: "#3 Andre",
|
||||
email: "andre.ortiz@example.com",
|
||||
phone: "08-3115-5776",
|
||||
id: "876838842",
|
||||
nat: "AU",
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
const result = getSourceDataKeysForEventAutocomplete(mockProps as any);
|
||||
const expected = undefined;
|
||||
const result = getKeysFromSourceDataForEventAutocomplete(mockProps as any);
|
||||
const expected = {
|
||||
currentItem: {
|
||||
gender: "",
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
id: "",
|
||||
nat: "",
|
||||
},
|
||||
};
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it("Should test with empty sourceDataKeys", () => {
|
||||
it("Should test with empty sourceData", () => {
|
||||
const mockProps = {
|
||||
sourceDataKeys: [],
|
||||
menuItemsSource: "DYANMIC",
|
||||
__evaluation__: {
|
||||
evaluatedValues: {
|
||||
sourceData: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = getSourceDataKeysForEventAutocomplete(mockProps as any);
|
||||
const expected = undefined;
|
||||
const result = getKeysFromSourceDataForEventAutocomplete(mockProps as any);
|
||||
const expected = { currentItem: {} };
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it("Should test without sourceData", () => {
|
||||
const mockProps = {};
|
||||
|
||||
const result = getKeysFromSourceDataForEventAutocomplete(mockProps as any);
|
||||
const expected = { currentItem: {} };
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,34 +1,41 @@
|
|||
import { isArray } from "lodash";
|
||||
import { MenuButtonWidgetProps, MenuItemsSource } from "../constants";
|
||||
|
||||
export const getSourceDataKeysForEventAutocomplete = (
|
||||
props: MenuButtonWidgetProps,
|
||||
export const getKeysFromSourceDataForEventAutocomplete = (
|
||||
sourceData?: Array<Record<string, unknown>> | unknown,
|
||||
) => {
|
||||
if (
|
||||
props.menuItemsSource === MenuItemsSource.STATIC ||
|
||||
!props.sourceDataKeys?.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (isArray(sourceData) && sourceData?.length) {
|
||||
const keys = getUniqueKeysFromSourceData(sourceData);
|
||||
|
||||
return {
|
||||
currentItem: props.sourceDataKeys.reduce(
|
||||
(prev, cur) => ({ ...prev, [cur]: "" }),
|
||||
{},
|
||||
),
|
||||
};
|
||||
return {
|
||||
currentItem: keys.reduce((prev, cur) => ({ ...prev, [cur]: "" }), {}),
|
||||
};
|
||||
} else {
|
||||
return { currentItem: {} };
|
||||
}
|
||||
};
|
||||
|
||||
export const getSourceDataKeys = (props: MenuButtonWidgetProps) => {
|
||||
if (!isArray(props.sourceData) || !props.sourceData?.length) {
|
||||
export const getUniqueKeysFromSourceData = (
|
||||
sourceData?: Array<Record<string, unknown>>,
|
||||
) => {
|
||||
if (!isArray(sourceData) || !sourceData?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const allKeys: string[] = [];
|
||||
|
||||
// get all keys
|
||||
props.sourceData?.forEach((item) => allKeys.push(...Object.keys(item)));
|
||||
sourceData?.forEach((item) => {
|
||||
if (isArray(item) && item?.length) {
|
||||
item.forEach((subItem) => {
|
||||
allKeys.push(...Object.keys(subItem));
|
||||
});
|
||||
} else {
|
||||
allKeys.push(...Object.keys(item));
|
||||
}
|
||||
});
|
||||
|
||||
// return unique keys
|
||||
return [...new Set(allKeys)];
|
||||
const uniqueKeys = [...new Set(allKeys)];
|
||||
|
||||
return uniqueKeys;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ import { MinimumPopupRows } from "widgets/constants";
|
|||
import { MenuButtonWidgetProps, MenuItem, MenuItemsSource } from "../constants";
|
||||
import contentConfig from "./propertyConfig/contentConfig";
|
||||
import styleConfig from "./propertyConfig/styleConfig";
|
||||
import equal from "fast-deep-equal/es6";
|
||||
import { isArray, orderBy } from "lodash";
|
||||
import { getSourceDataKeys } from "./helper";
|
||||
import { Stylesheet } from "entities/AppTheming";
|
||||
|
||||
class MenuButtonWidget extends BaseWidget<MenuButtonWidgetProps, WidgetState> {
|
||||
|
|
@ -108,19 +106,6 @@ class MenuButtonWidget extends BaseWidget<MenuButtonWidgetProps, WidgetState> {
|
|||
return [];
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
super.updateWidgetProperty("sourceDataKeys", getSourceDataKeys(this.props));
|
||||
};
|
||||
|
||||
componentDidUpdate = (prevProps: MenuButtonWidgetProps) => {
|
||||
if (!equal(prevProps.sourceData, this.props.sourceData)) {
|
||||
super.updateWidgetProperty(
|
||||
"sourceDataKeys",
|
||||
getSourceDataKeys(this.props),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
getPageView() {
|
||||
const { componentWidth } = this.getComponentDimensions();
|
||||
const menuDropDownWidth = MinimumPopupRows * this.props.parentColumnSpace;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,11 @@
|
|||
import { ValidationTypes } from "constants/WidgetValidation";
|
||||
import { ICON_NAMES } from "../../../constants";
|
||||
import { getSourceDataKeysForEventAutocomplete } from "../../helper";
|
||||
import { ICON_NAMES, MenuButtonWidgetProps } from "../../../constants";
|
||||
import { getKeysFromSourceDataForEventAutocomplete } from "../../helper";
|
||||
|
||||
export default {
|
||||
editableTitle: false,
|
||||
titlePropertyName: "label",
|
||||
panelIdPropertyName: "id",
|
||||
updateHook: (props: any, propertyPath: string, propertyValue: string) => {
|
||||
return [
|
||||
{
|
||||
propertyPath,
|
||||
propertyValue,
|
||||
},
|
||||
];
|
||||
},
|
||||
contentChildren: [
|
||||
{
|
||||
sectionName: "General",
|
||||
|
|
@ -33,7 +25,7 @@ export default {
|
|||
type: ValidationTypes.TEXT,
|
||||
},
|
||||
},
|
||||
dependencies: ["sourceDataKeys"],
|
||||
evaluatedDependencies: ["sourceData"],
|
||||
},
|
||||
{
|
||||
propertyName: "isVisible",
|
||||
|
|
@ -51,7 +43,7 @@ export default {
|
|||
},
|
||||
},
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
dependencies: ["sourceDataKeys"],
|
||||
evaluatedDependencies: ["sourceData"],
|
||||
},
|
||||
{
|
||||
propertyName: "isDisabled",
|
||||
|
|
@ -69,7 +61,7 @@ export default {
|
|||
},
|
||||
},
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
dependencies: ["sourceDataKeys"],
|
||||
evaluatedDependencies: ["sourceData"],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -85,8 +77,12 @@ export default {
|
|||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: true,
|
||||
additionalAutoComplete: getSourceDataKeysForEventAutocomplete,
|
||||
dependencies: ["sourceDataKeys"],
|
||||
additionalAutoComplete: (props: MenuButtonWidgetProps) => {
|
||||
return getKeysFromSourceDataForEventAutocomplete(
|
||||
props?.__evaluation__?.evaluatedValues?.sourceData,
|
||||
);
|
||||
},
|
||||
evaluatedDependencies: ["sourceData"],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -114,7 +110,7 @@ export default {
|
|||
},
|
||||
},
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
dependencies: ["sourceDataKeys"],
|
||||
evaluatedDependencies: ["sourceData"],
|
||||
},
|
||||
{
|
||||
propertyName: "iconAlign",
|
||||
|
|
@ -145,7 +141,7 @@ export default {
|
|||
},
|
||||
},
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
dependencies: ["sourceDataKeys"],
|
||||
evaluatedDependencies: ["sourceData"],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -162,7 +158,7 @@ export default {
|
|||
isTriggerProperty: false,
|
||||
isJSConvertible: true,
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
dependencies: ["sourceDataKeys"],
|
||||
evaluatedDependencies: ["sourceData"],
|
||||
validation: {
|
||||
type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE,
|
||||
params: {
|
||||
|
|
@ -181,7 +177,7 @@ export default {
|
|||
isTriggerProperty: false,
|
||||
isJSConvertible: true,
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
dependencies: ["sourceDataKeys"],
|
||||
evaluatedDependencies: ["sourceData"],
|
||||
validation: {
|
||||
type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE,
|
||||
params: {
|
||||
|
|
@ -200,7 +196,7 @@ export default {
|
|||
isTriggerProperty: false,
|
||||
isJSConvertible: true,
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
dependencies: ["sourceDataKeys"],
|
||||
evaluatedDependencies: ["sourceData"],
|
||||
validation: {
|
||||
type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE,
|
||||
params: {
|
||||
|
|
|
|||
|
|
@ -19,10 +19,6 @@ export const updateMenuItemsSource = (
|
|||
propertyPath: "sourceData",
|
||||
propertyValue: [],
|
||||
});
|
||||
propertiesToUpdate.push({
|
||||
propertyPath: "sourceDataKeys",
|
||||
propertyValue: [],
|
||||
});
|
||||
}
|
||||
|
||||
if (!props.configureMenuItems) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@ import {
|
|||
ButtonVariant,
|
||||
} from "components/constants";
|
||||
import { DropdownOption } from "widgets/SelectWidget/constants";
|
||||
import {
|
||||
ConfigureMenuItems,
|
||||
MenuItem,
|
||||
MenuItems,
|
||||
MenuItemsSource,
|
||||
} from "widgets/MenuButtonWidget/constants";
|
||||
import { ColumnTypes } from "../constants";
|
||||
|
||||
export type TableSizes = {
|
||||
|
|
@ -155,6 +161,9 @@ export interface MenuButtonCellProperties {
|
|||
menuColor?: string;
|
||||
menuButtoniconName?: IconName;
|
||||
onItemClicked?: (onClick: string | undefined) => void;
|
||||
menuItemsSource: MenuItemsSource;
|
||||
configureMenuItems: ConfigureMenuItems;
|
||||
sourceData?: Array<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
export interface URLCellProperties {
|
||||
|
|
@ -199,24 +208,6 @@ export interface CellLayoutProperties
|
|||
ImageCellProperties,
|
||||
BaseCellProperties {}
|
||||
|
||||
export type MenuItems = Record<
|
||||
string,
|
||||
{
|
||||
widgetId: string;
|
||||
id: string;
|
||||
index: number;
|
||||
isVisible?: boolean;
|
||||
isDisabled?: boolean;
|
||||
label?: string;
|
||||
backgroundColor?: string;
|
||||
textColor?: string;
|
||||
iconName?: IconName;
|
||||
iconColor?: string;
|
||||
iconAlign?: Alignment;
|
||||
onClick?: string;
|
||||
}
|
||||
>;
|
||||
|
||||
export interface TableColumnMetaProps {
|
||||
isHidden: boolean;
|
||||
format?: string;
|
||||
|
|
@ -339,6 +330,10 @@ export interface ColumnProperties
|
|||
onItemClicked?: (onClick: string | undefined) => void;
|
||||
iconButtonStyle?: ButtonStyleType;
|
||||
imageSize?: ImageSize;
|
||||
getVisibleItems?: () => Array<MenuItem>;
|
||||
menuItemsSource?: MenuItemsSource;
|
||||
configureMenuItems?: ConfigureMenuItems;
|
||||
sourceData?: Array<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
export const ConditionFunctions: {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,17 @@ import React from "react";
|
|||
import { IconName } from "@blueprintjs/icons";
|
||||
import { Alignment } from "@blueprintjs/core";
|
||||
|
||||
import { BaseCellComponentProps, MenuItems } from "../Constants";
|
||||
import { BaseCellComponentProps } from "../Constants";
|
||||
import { ButtonVariant } from "components/constants";
|
||||
import { CellWrapper } from "../TableStyledWrappers";
|
||||
import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl";
|
||||
import MenuButtonTableComponent from "./menuButtonTableComponent";
|
||||
import {
|
||||
ConfigureMenuItems,
|
||||
MenuItem,
|
||||
MenuItems,
|
||||
MenuItemsSource,
|
||||
} from "widgets/MenuButtonWidget/constants";
|
||||
|
||||
interface MenuButtonProps extends Omit<RenderMenuButtonProps, "columnActions"> {
|
||||
action?: ColumnAction;
|
||||
|
|
@ -16,6 +22,8 @@ function MenuButton({
|
|||
borderRadius,
|
||||
boxShadow,
|
||||
compactMode,
|
||||
configureMenuItems,
|
||||
getVisibleItems,
|
||||
iconAlign,
|
||||
iconName,
|
||||
isCompact,
|
||||
|
|
@ -24,9 +32,11 @@ function MenuButton({
|
|||
label,
|
||||
menuColor,
|
||||
menuItems,
|
||||
menuItemsSource,
|
||||
menuVariant,
|
||||
onCommandClick,
|
||||
rowIndex,
|
||||
sourceData,
|
||||
}: MenuButtonProps): JSX.Element {
|
||||
const handlePropagation = (
|
||||
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
|
||||
|
|
@ -35,9 +45,9 @@ function MenuButton({
|
|||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
const onItemClicked = (onClick?: string) => {
|
||||
const onItemClicked = (onClick?: string, index?: number) => {
|
||||
if (onClick) {
|
||||
onCommandClick(onClick);
|
||||
onCommandClick(onClick, index);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -47,6 +57,8 @@ function MenuButton({
|
|||
borderRadius={borderRadius}
|
||||
boxShadow={boxShadow}
|
||||
compactMode={compactMode}
|
||||
configureMenuItems={configureMenuItems}
|
||||
getVisibleItems={getVisibleItems}
|
||||
iconAlign={iconAlign}
|
||||
iconName={iconName}
|
||||
isCompact={isCompact}
|
||||
|
|
@ -54,9 +66,11 @@ function MenuButton({
|
|||
label={label}
|
||||
menuColor={menuColor}
|
||||
menuItems={{ ...menuItems }}
|
||||
menuItemsSource={menuItemsSource}
|
||||
menuVariant={menuVariant}
|
||||
onItemClicked={onItemClicked}
|
||||
rowIndex={rowIndex}
|
||||
sourceData={sourceData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -66,7 +80,11 @@ export interface RenderMenuButtonProps extends BaseCellComponentProps {
|
|||
isSelected: boolean;
|
||||
label: string;
|
||||
isDisabled: boolean;
|
||||
onCommandClick: (dynamicTrigger: string, onComplete?: () => void) => void;
|
||||
onCommandClick: (
|
||||
dynamicTrigger: string,
|
||||
index?: number,
|
||||
onComplete?: () => void,
|
||||
) => void;
|
||||
isCompact?: boolean;
|
||||
menuItems: MenuItems;
|
||||
menuVariant?: ButtonVariant;
|
||||
|
|
@ -76,6 +94,10 @@ export interface RenderMenuButtonProps extends BaseCellComponentProps {
|
|||
iconName?: IconName;
|
||||
iconAlign?: Alignment;
|
||||
rowIndex: number;
|
||||
getVisibleItems: (rowIndex: number) => Array<MenuItem>;
|
||||
menuItemsSource: MenuItemsSource;
|
||||
configureMenuItems: ConfigureMenuItems;
|
||||
sourceData?: Array<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
export function MenuButtonCell(props: RenderMenuButtonProps) {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import styled, { createGlobalStyle } from "styled-components";
|
|||
import {
|
||||
Alignment,
|
||||
Button,
|
||||
Classes as CoreClasses,
|
||||
Classes as BlueprintCoreClasses,
|
||||
Icon,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Classes as BClasses,
|
||||
MenuItem as BlueprintMenuItem,
|
||||
Classes as BlueprintClasses,
|
||||
} from "@blueprintjs/core";
|
||||
import { Classes, Popover2 } from "@blueprintjs/popover2";
|
||||
import { IconName } from "@blueprintjs/icons";
|
||||
|
|
@ -20,15 +20,19 @@ import {
|
|||
} from "widgets/WidgetUtils";
|
||||
import { darkenActive, darkenHover } from "constants/DefaultTheme";
|
||||
import { ButtonVariant, ButtonVariantTypes } from "components/constants";
|
||||
import { MenuItems } from "../Constants";
|
||||
import tinycolor from "tinycolor2";
|
||||
import { Colors } from "constants/Colors";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import {
|
||||
getBooleanPropertyValue,
|
||||
getPropertyValue,
|
||||
} from "widgets/TableWidgetV2/widget/utilities";
|
||||
import { ThemeProp } from "widgets/constants";
|
||||
import {
|
||||
ConfigureMenuItems,
|
||||
MenuItem,
|
||||
MenuItems,
|
||||
MenuItemsSource,
|
||||
} from "widgets/MenuButtonWidget/constants";
|
||||
|
||||
const MenuButtonContainer = styled.div`
|
||||
width: 100%;
|
||||
|
|
@ -54,7 +58,7 @@ const PopoverStyles = createGlobalStyle<{
|
|||
borderRadius >= `1.5rem` ? `0.375rem` : borderRadius};
|
||||
overflow: hidden;
|
||||
}
|
||||
& .${BClasses.MENU_ITEM} {
|
||||
& .${BlueprintClasses.MENU_ITEM} {
|
||||
padding: 9px 12px;
|
||||
border-radius: 0;
|
||||
&:hover {
|
||||
|
|
@ -153,8 +157,8 @@ const BaseButton = styled(Button)<ThemeProp & BaseStyleProps>`
|
|||
box-shadow: ${({ boxShadow }) => `${boxShadow}`} !important;
|
||||
`;
|
||||
|
||||
const BaseMenuItem = styled(MenuItem)<ThemeProp & BaseStyleProps>`
|
||||
&.${CoreClasses.MENU_ITEM}.${CoreClasses.DISABLED} {
|
||||
const BaseMenuItem = styled(BlueprintMenuItem)<ThemeProp & BaseStyleProps>`
|
||||
&.${BlueprintCoreClasses.MENU_ITEM}.${BlueprintCoreClasses.DISABLED} {
|
||||
background-color: ${Colors.GREY_1} !important;
|
||||
}
|
||||
${({ backgroundColor, theme }) =>
|
||||
|
|
@ -206,64 +210,71 @@ const StyledMenu = styled(Menu)`
|
|||
|
||||
interface PopoverContentProps {
|
||||
menuItems: MenuItems;
|
||||
onItemClicked: (onClick: string | undefined) => void;
|
||||
onItemClicked: (
|
||||
onClick: string | undefined,
|
||||
index?: number,
|
||||
onComplete?: () => void,
|
||||
) => void;
|
||||
getVisibleItems: (rowIndex: number) => Array<MenuItem>;
|
||||
isCompact?: boolean;
|
||||
rowIndex: number;
|
||||
menuItemsSource: MenuItemsSource;
|
||||
configureMenuItems: ConfigureMenuItems;
|
||||
sourceData?: Array<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
function PopoverContent(props: PopoverContentProps) {
|
||||
const { isCompact, menuItems: itemsObj, onItemClicked, rowIndex } = props;
|
||||
const { getVisibleItems, isCompact, onItemClicked, rowIndex } = props;
|
||||
|
||||
if (!itemsObj) return <StyledMenu />;
|
||||
const visibleItems = Object.keys(itemsObj)
|
||||
.map((itemKey) => itemsObj[itemKey])
|
||||
.filter((item) => getBooleanPropertyValue(item.isVisible, rowIndex));
|
||||
const visibleItems = getVisibleItems(rowIndex);
|
||||
|
||||
const items = orderBy(visibleItems, ["index"], ["asc"]);
|
||||
if (!visibleItems?.length) {
|
||||
return <StyledMenu />;
|
||||
} else {
|
||||
const listItems = visibleItems.map((item: MenuItem, index: number) => {
|
||||
const {
|
||||
backgroundColor,
|
||||
iconAlign,
|
||||
iconColor,
|
||||
iconName,
|
||||
id,
|
||||
isDisabled,
|
||||
label,
|
||||
onClick,
|
||||
textColor,
|
||||
} = item;
|
||||
|
||||
const listItems = items.map((menuItem) => {
|
||||
const {
|
||||
backgroundColor,
|
||||
iconAlign,
|
||||
iconColor,
|
||||
iconName,
|
||||
id,
|
||||
isDisabled,
|
||||
label,
|
||||
onClick,
|
||||
textColor,
|
||||
} = menuItem;
|
||||
return (
|
||||
<BaseMenuItem
|
||||
backgroundColor={
|
||||
getPropertyValue(backgroundColor, rowIndex) || "#FFFFFF"
|
||||
}
|
||||
disabled={getBooleanPropertyValue(isDisabled, rowIndex)}
|
||||
icon={
|
||||
iconAlign !== Alignment.RIGHT && iconName ? (
|
||||
<Icon color={iconColor} icon={iconName} />
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
}
|
||||
isCompact={isCompact}
|
||||
key={id}
|
||||
labelElement={
|
||||
iconAlign === Alignment.RIGHT && iconName ? (
|
||||
<Icon color={iconColor} icon={iconName} />
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
}
|
||||
onClick={() => onItemClicked(onClick, index)}
|
||||
text={label}
|
||||
textColor={getPropertyValue(textColor, rowIndex)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<BaseMenuItem
|
||||
backgroundColor={
|
||||
getPropertyValue(backgroundColor, rowIndex) || "#FFFFFF"
|
||||
}
|
||||
disabled={getBooleanPropertyValue(isDisabled, rowIndex)}
|
||||
icon={
|
||||
iconAlign !== Alignment.RIGHT ? (
|
||||
<Icon color={iconColor} icon={iconName || undefined} />
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
}
|
||||
isCompact={isCompact}
|
||||
key={id}
|
||||
labelElement={
|
||||
iconAlign === Alignment.RIGHT ? (
|
||||
<Icon color={iconColor} icon={iconName || undefined} />
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
}
|
||||
onClick={() => onItemClicked(onClick)}
|
||||
text={label}
|
||||
textColor={getPropertyValue(textColor, rowIndex)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return <StyledMenu>{listItems}</StyledMenu>;
|
||||
return <StyledMenu>{listItems}</StyledMenu>;
|
||||
}
|
||||
}
|
||||
|
||||
interface PopoverTargetButtonProps {
|
||||
|
|
@ -315,6 +326,7 @@ export interface MenuButtonComponentProps {
|
|||
isVisible?: boolean;
|
||||
isCompact?: boolean;
|
||||
menuItems: MenuItems;
|
||||
getVisibleItems: (rowIndex: number) => Array<MenuItem>;
|
||||
menuVariant?: ButtonVariant;
|
||||
menuColor?: string;
|
||||
borderRadius?: string;
|
||||
|
|
@ -322,9 +334,16 @@ export interface MenuButtonComponentProps {
|
|||
boxShadowColor?: string;
|
||||
iconName?: IconName;
|
||||
iconAlign?: Alignment;
|
||||
onItemClicked: (onClick: string | undefined) => void;
|
||||
onItemClicked: (
|
||||
onClick: string | undefined,
|
||||
index?: number,
|
||||
onComplete?: () => void,
|
||||
) => void;
|
||||
rowIndex: number;
|
||||
compactMode?: string;
|
||||
menuItemsSource: MenuItemsSource;
|
||||
configureMenuItems: ConfigureMenuItems;
|
||||
sourceData?: Array<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
function MenuButtonTableComponent(props: MenuButtonComponentProps) {
|
||||
|
|
@ -333,6 +352,8 @@ function MenuButtonTableComponent(props: MenuButtonComponentProps) {
|
|||
boxShadow,
|
||||
boxShadowColor,
|
||||
compactMode,
|
||||
configureMenuItems,
|
||||
getVisibleItems,
|
||||
iconAlign,
|
||||
iconName,
|
||||
isCompact,
|
||||
|
|
@ -340,9 +361,11 @@ function MenuButtonTableComponent(props: MenuButtonComponentProps) {
|
|||
label,
|
||||
menuColor = "#e1e1e1",
|
||||
menuItems,
|
||||
menuItemsSource,
|
||||
menuVariant,
|
||||
onItemClicked,
|
||||
rowIndex,
|
||||
sourceData,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
|
|
@ -359,10 +382,14 @@ function MenuButtonTableComponent(props: MenuButtonComponentProps) {
|
|||
}}
|
||||
content={
|
||||
<PopoverContent
|
||||
configureMenuItems={configureMenuItems}
|
||||
getVisibleItems={getVisibleItems}
|
||||
isCompact={isCompact}
|
||||
menuItems={menuItems}
|
||||
menuItemsSource={menuItemsSource}
|
||||
onItemClicked={onItemClicked}
|
||||
rowIndex={rowIndex}
|
||||
sourceData={sourceData}
|
||||
/>
|
||||
}
|
||||
disabled={isDisabled}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import _, {
|
|||
isEmpty,
|
||||
union,
|
||||
isObject,
|
||||
orderBy,
|
||||
} from "lodash";
|
||||
|
||||
import BaseWidget, { WidgetState } from "widgets/BaseWidget";
|
||||
|
|
@ -58,6 +59,7 @@ import {
|
|||
getCellProperties,
|
||||
isColumnTypeEditable,
|
||||
getColumnType,
|
||||
getBooleanPropertyValue,
|
||||
} from "./utilities";
|
||||
import {
|
||||
ColumnProperties,
|
||||
|
|
@ -85,6 +87,7 @@ import { SwitchCell } from "../component/cellComponents/SwitchCell";
|
|||
import { SelectCell } from "../component/cellComponents/SelectCell";
|
||||
import { CellWrapper } from "../component/TableStyledWrappers";
|
||||
import { Stylesheet } from "entities/AppTheming";
|
||||
import { MenuItem, MenuItemsSource } from "widgets/MenuButtonWidget/constants";
|
||||
|
||||
const ReactTableComponent = lazy(() =>
|
||||
retryPromise(() => import("../component")),
|
||||
|
|
@ -1585,6 +1588,70 @@ class TableWidgetV2 extends BaseWidget<TableWidgetProps, WidgetState> {
|
|||
);
|
||||
|
||||
case ColumnTypes.MENU_BUTTON:
|
||||
const getVisibleItems = (rowIndex: number) => {
|
||||
const {
|
||||
configureMenuItems,
|
||||
menuItems,
|
||||
menuItemsSource,
|
||||
sourceData,
|
||||
} = cellProperties;
|
||||
|
||||
if (menuItemsSource === MenuItemsSource.STATIC && menuItems) {
|
||||
const visibleItems = Object.values(menuItems)?.filter((item) =>
|
||||
getBooleanPropertyValue(item.isVisible, rowIndex),
|
||||
);
|
||||
|
||||
return visibleItems?.length
|
||||
? orderBy(visibleItems, ["index"], ["asc"])
|
||||
: [];
|
||||
} else if (
|
||||
menuItemsSource === MenuItemsSource.DYNAMIC &&
|
||||
isArray(sourceData) &&
|
||||
sourceData?.length &&
|
||||
configureMenuItems?.config
|
||||
) {
|
||||
const { config } = configureMenuItems;
|
||||
|
||||
const getValue = (
|
||||
propertyName: keyof MenuItem,
|
||||
index: number,
|
||||
rowIndex: number,
|
||||
) => {
|
||||
const value = config[propertyName];
|
||||
|
||||
if (isArray(value) && isArray(value[rowIndex])) {
|
||||
return value[rowIndex][index];
|
||||
} else if (isArray(value)) {
|
||||
return value[index];
|
||||
}
|
||||
|
||||
return value ?? null;
|
||||
};
|
||||
|
||||
const visibleItems = sourceData
|
||||
.map((item, index) => ({
|
||||
...item,
|
||||
id: index.toString(),
|
||||
isVisible: getValue("isVisible", index, rowIndex),
|
||||
isDisabled: getValue("isDisabled", index, rowIndex),
|
||||
index: index,
|
||||
widgetId: "",
|
||||
label: getValue("label", index, rowIndex),
|
||||
onClick: config?.onClick,
|
||||
textColor: getValue("textColor", index, rowIndex),
|
||||
backgroundColor: getValue("backgroundColor", index, rowIndex),
|
||||
iconAlign: getValue("iconAlign", index, rowIndex),
|
||||
iconColor: getValue("iconColor", index, rowIndex),
|
||||
iconName: getValue("iconName", index, rowIndex),
|
||||
}))
|
||||
.filter((item) => item.isVisible === true);
|
||||
|
||||
return visibleItems;
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuButtonCell
|
||||
allowCellWrapping={cellProperties.allowCellWrapping}
|
||||
|
|
@ -1594,7 +1661,9 @@ class TableWidgetV2 extends BaseWidget<TableWidgetProps, WidgetState> {
|
|||
boxShadow={cellProperties.boxShadow}
|
||||
cellBackground={cellProperties.cellBackground}
|
||||
compactMode={compactMode}
|
||||
configureMenuItems={cellProperties.configureMenuItems}
|
||||
fontStyle={cellProperties.fontStyle}
|
||||
getVisibleItems={getVisibleItems}
|
||||
horizontalAlignment={cellProperties.horizontalAlignment}
|
||||
iconAlign={cellProperties.iconAlign}
|
||||
iconName={cellProperties.menuButtoniconName || undefined}
|
||||
|
|
@ -1609,17 +1678,34 @@ class TableWidgetV2 extends BaseWidget<TableWidgetProps, WidgetState> {
|
|||
cellProperties.menuColor || this.props.accentColor || Colors.GREEN
|
||||
}
|
||||
menuItems={cellProperties.menuItems}
|
||||
menuItemsSource={cellProperties.menuItemsSource}
|
||||
menuVariant={cellProperties.menuVariant ?? DEFAULT_MENU_VARIANT}
|
||||
onCommandClick={(action: string, onComplete?: () => void) =>
|
||||
this.onColumnEvent({
|
||||
onCommandClick={(
|
||||
action: string,
|
||||
index?: number,
|
||||
onComplete?: () => void,
|
||||
) => {
|
||||
const additionalData: Record<
|
||||
string,
|
||||
string | number | Record<string, unknown>
|
||||
> = {};
|
||||
|
||||
if (cellProperties?.sourceData && _.isNumber(index)) {
|
||||
additionalData.currentItem = cellProperties.sourceData[index];
|
||||
additionalData.currentIndex = index;
|
||||
}
|
||||
|
||||
return this.onColumnEvent({
|
||||
rowIndex,
|
||||
action,
|
||||
onComplete,
|
||||
triggerPropertyName: "onClick",
|
||||
eventType: EventType.ON_CLICK,
|
||||
})
|
||||
}
|
||||
additionalData,
|
||||
});
|
||||
}}
|
||||
rowIndex={originalIndex}
|
||||
sourceData={cellProperties.sourceData}
|
||||
textColor={cellProperties.textColor}
|
||||
textSize={cellProperties.textSize}
|
||||
verticalAlignment={cellProperties.verticalAlignment}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,19 @@ import {
|
|||
ICON_NAMES,
|
||||
TableWidgetProps,
|
||||
} from "widgets/TableWidgetV2/constants";
|
||||
import { hideByColumnType, updateIconAlignment } from "../../propertyUtils";
|
||||
import {
|
||||
hideByColumnType,
|
||||
hideByMenuItemsSource,
|
||||
hideIfMenuItemsSourceDataIsFalsy,
|
||||
updateIconAlignment,
|
||||
updateMenuItemsSource,
|
||||
} from "../../propertyUtils";
|
||||
import { IconNames } from "@blueprintjs/icons";
|
||||
import { MenuItemsSource } from "widgets/MenuButtonWidget/constants";
|
||||
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
||||
import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService";
|
||||
import { sourceDataArrayValidation } from "widgets/MenuButtonWidget/validations";
|
||||
import configureMenuItemsConfig from "./childPanels/configureMenuItemsConfig";
|
||||
|
||||
export default {
|
||||
sectionName: "Basic",
|
||||
|
|
@ -70,6 +81,104 @@ export default {
|
|||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
},
|
||||
{
|
||||
propertyName: "menuItemsSource",
|
||||
helpText: "Sets the source for the menu items",
|
||||
label: "Menu Items Source",
|
||||
controlType: "ICON_TABS",
|
||||
fullWidth: true,
|
||||
defaultValue: MenuItemsSource.STATIC,
|
||||
options: [
|
||||
{
|
||||
label: "Static",
|
||||
value: MenuItemsSource.STATIC,
|
||||
},
|
||||
{
|
||||
label: "Dynamic",
|
||||
value: MenuItemsSource.DYNAMIC,
|
||||
},
|
||||
],
|
||||
isJSConvertible: false,
|
||||
isBindProperty: false,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.TEXT },
|
||||
updateHook: updateMenuItemsSource,
|
||||
dependencies: [
|
||||
"primaryColumns",
|
||||
"columnOrder",
|
||||
"sourceData",
|
||||
"configureMenuItems",
|
||||
],
|
||||
hidden: (props: TableWidgetProps, propertyPath: string) => {
|
||||
return hideByColumnType(
|
||||
props,
|
||||
propertyPath,
|
||||
[ColumnTypes.MENU_BUTTON],
|
||||
false,
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
helpText: "Takes in an array of items to display the menu items.",
|
||||
propertyName: "sourceData",
|
||||
label: "Source Data",
|
||||
controlType: "TABLE_COMPUTE_VALUE",
|
||||
placeholderText: "{{Query1.data}}",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: {
|
||||
type: ValidationTypes.FUNCTION,
|
||||
params: {
|
||||
expected: {
|
||||
type: "Array of values",
|
||||
example: `['option1', 'option2'] | [{ "label": "label1", "value": "value1" }]`,
|
||||
autocompleteDataType: AutocompleteDataType.ARRAY,
|
||||
},
|
||||
fnString: sourceDataArrayValidation.toString(),
|
||||
},
|
||||
},
|
||||
evaluationSubstitutionType: EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
||||
hidden: (props: TableWidgetProps, propertyPath: string) => {
|
||||
return (
|
||||
hideByColumnType(
|
||||
props,
|
||||
propertyPath,
|
||||
[ColumnTypes.MENU_BUTTON],
|
||||
false,
|
||||
) ||
|
||||
hideByMenuItemsSource(props, propertyPath, MenuItemsSource.STATIC)
|
||||
);
|
||||
},
|
||||
dependencies: ["primaryColumns", "columnOrder", "menuItemsSource"],
|
||||
},
|
||||
{
|
||||
helpText: "Configure how each menu item will appear.",
|
||||
propertyName: "configureMenuItems",
|
||||
controlType: "OPEN_CONFIG_PANEL",
|
||||
buttonConfig: {
|
||||
label: "Item Configuration",
|
||||
icon: "settings-2-line",
|
||||
},
|
||||
label: "Configure Menu Items",
|
||||
isBindProperty: false,
|
||||
isTriggerProperty: false,
|
||||
hidden: (props: TableWidgetProps, propertyPath: string) =>
|
||||
hideByColumnType(
|
||||
props,
|
||||
propertyPath,
|
||||
[ColumnTypes.MENU_BUTTON],
|
||||
false,
|
||||
) ||
|
||||
hideIfMenuItemsSourceDataIsFalsy(props, propertyPath) ||
|
||||
hideByMenuItemsSource(props, propertyPath, MenuItemsSource.STATIC),
|
||||
dependencies: [
|
||||
"primaryColumns",
|
||||
"columnOrder",
|
||||
"menuItemsSource",
|
||||
"sourceData",
|
||||
],
|
||||
panelConfig: configureMenuItemsConfig,
|
||||
},
|
||||
{
|
||||
helpText: "Menu items",
|
||||
propertyName: "menuItems",
|
||||
|
|
@ -78,11 +187,14 @@ export default {
|
|||
isBindProperty: false,
|
||||
isTriggerProperty: false,
|
||||
hidden: (props: TableWidgetProps, propertyPath: string) => {
|
||||
return hideByColumnType(
|
||||
props,
|
||||
propertyPath,
|
||||
[ColumnTypes.MENU_BUTTON],
|
||||
false,
|
||||
return (
|
||||
hideByColumnType(
|
||||
props,
|
||||
propertyPath,
|
||||
[ColumnTypes.MENU_BUTTON],
|
||||
false,
|
||||
) ||
|
||||
hideByMenuItemsSource(props, propertyPath, MenuItemsSource.DYNAMIC)
|
||||
);
|
||||
},
|
||||
dependencies: ["primaryColumns", "columnOrder"],
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
hideByColumnType,
|
||||
showByColumnType,
|
||||
uniqueColumnAliasValidation,
|
||||
updateMenuItemsSource,
|
||||
updateNumberColumnTypeTextAlignment,
|
||||
updateThemeStylesheetsInColumns,
|
||||
} from "../../propertyUtils";
|
||||
|
|
@ -78,6 +79,7 @@ export default {
|
|||
updateHook: composePropertyUpdateHook([
|
||||
updateNumberColumnTypeTextAlignment,
|
||||
updateThemeStylesheetsInColumns,
|
||||
updateMenuItemsSource,
|
||||
]),
|
||||
dependencies: ["primaryColumns", "columnOrder", "childStylesheet"],
|
||||
isBindProperty: false,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,216 @@
|
|||
import { ValidationTypes } from "constants/WidgetValidation";
|
||||
import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService";
|
||||
import { ICON_NAMES } from "widgets/MenuButtonWidget/constants";
|
||||
import {
|
||||
booleanForEachRowValidation,
|
||||
colorForEachRowValidation,
|
||||
iconNamesForEachRowValidation,
|
||||
iconPositionForEachRowValidation,
|
||||
textForEachRowValidation,
|
||||
} from "widgets/MenuButtonWidget/validations";
|
||||
import { getSourceDataAndCaluclateKeysForEventAutoComplete } from "widgets/TableWidgetV2/widget/utilities";
|
||||
|
||||
export default {
|
||||
editableTitle: false,
|
||||
titlePropertyName: "label",
|
||||
panelIdPropertyName: "id",
|
||||
contentChildren: [
|
||||
{
|
||||
sectionName: "General",
|
||||
children: [
|
||||
{
|
||||
propertyName: "label",
|
||||
helpText:
|
||||
"Sets the label of a menu item using the {{currentItem}} binding.",
|
||||
label: "Label",
|
||||
controlType: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
placeholderText: "{{currentItem.name}}",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: {
|
||||
type: ValidationTypes.FUNCTION,
|
||||
params: {
|
||||
expected: {
|
||||
type: "Array of values",
|
||||
example: `['option1', 'option2'] | [{ "label": "label1", "value": "value1" }]`,
|
||||
autocompleteDataType: AutocompleteDataType.ARRAY,
|
||||
},
|
||||
fnString: textForEachRowValidation.toString(),
|
||||
},
|
||||
},
|
||||
evaluatedDependencies: ["primaryColumns"],
|
||||
},
|
||||
{
|
||||
propertyName: "isVisible",
|
||||
helpText:
|
||||
"Controls the visibility of the widget. Can also be configured the using {{currentItem}} binding.",
|
||||
label: "Visible",
|
||||
controlType: "SWITCH",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: {
|
||||
type: ValidationTypes.FUNCTION,
|
||||
params: {
|
||||
fnString: booleanForEachRowValidation.toString(),
|
||||
},
|
||||
},
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
evaluatedDependencies: ["primaryColumns"],
|
||||
},
|
||||
{
|
||||
propertyName: "isDisabled",
|
||||
helpText:
|
||||
"Disables input to the widget. Can also be configured the using {{currentItem}} binding.",
|
||||
label: "Disabled",
|
||||
controlType: "SWITCH",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: {
|
||||
type: ValidationTypes.FUNCTION,
|
||||
params: {
|
||||
fnString: booleanForEachRowValidation.toString(),
|
||||
},
|
||||
},
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
evaluatedDependencies: ["primaryColumns"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "Events",
|
||||
children: [
|
||||
{
|
||||
helpText:
|
||||
"Triggers an action when the menu item is clicked. Can also be configured the using {{currentItem}} binding.",
|
||||
propertyName: "onClick",
|
||||
label: "onClick",
|
||||
controlType: "ACTION_SELECTOR",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: true,
|
||||
additionalAutoComplete: getSourceDataAndCaluclateKeysForEventAutoComplete,
|
||||
evaluatedDependencies: ["primaryColumns"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
styleChildren: [
|
||||
{
|
||||
sectionName: "Icon",
|
||||
children: [
|
||||
{
|
||||
propertyName: "iconName",
|
||||
label: "Icon",
|
||||
helpText:
|
||||
"Sets the icon to be used for a menu item. Can also be configured the using {{currentItem}} binding.",
|
||||
controlType: "ICON_SELECT",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
isJSConvertible: true,
|
||||
validation: {
|
||||
type: ValidationTypes.FUNCTION,
|
||||
params: {
|
||||
allowedValues: ICON_NAMES,
|
||||
fnString: iconNamesForEachRowValidation.toString(),
|
||||
},
|
||||
},
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
evaluatedDependencies: ["primaryColumns"],
|
||||
},
|
||||
{
|
||||
propertyName: "iconAlign",
|
||||
label: "Position",
|
||||
helpText:
|
||||
"Sets the icon alignment of a menu item. Can also be configured the using {{currentItem}} binding.",
|
||||
controlType: "ICON_TABS",
|
||||
options: [
|
||||
{
|
||||
icon: "VERTICAL_LEFT",
|
||||
value: "left",
|
||||
},
|
||||
{
|
||||
icon: "VERTICAL_RIGHT",
|
||||
value: "right",
|
||||
},
|
||||
],
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
isJSConvertible: true,
|
||||
validation: {
|
||||
type: ValidationTypes.FUNCTION,
|
||||
params: {
|
||||
allowedValues: ["center", "left", "right"],
|
||||
fnString: iconPositionForEachRowValidation.toString(),
|
||||
},
|
||||
},
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
evaluatedDependencies: ["primaryColumns"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "Color",
|
||||
children: [
|
||||
{
|
||||
propertyName: "iconColor",
|
||||
helpText:
|
||||
"Sets the icon color of a menu item. Can also be configured the using {{currentItem}} binding.",
|
||||
label: "Icon color",
|
||||
controlType: "COLOR_PICKER",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
isJSConvertible: true,
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
evaluatedDependencies: ["primaryColumns"],
|
||||
validation: {
|
||||
type: ValidationTypes.FUNCTION,
|
||||
params: {
|
||||
regex: /^(?![<|{{]).+/,
|
||||
fnString: colorForEachRowValidation.toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
propertyName: "backgroundColor",
|
||||
helpText:
|
||||
"Sets the background color of a menu item. Can also be configured the using {{currentItem}} binding.",
|
||||
label: "Background color",
|
||||
controlType: "COLOR_PICKER",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
isJSConvertible: true,
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
evaluatedDependencies: ["primaryColumns"],
|
||||
validation: {
|
||||
type: ValidationTypes.FUNCTION,
|
||||
params: {
|
||||
regex: /^(?![<|{{]).+/,
|
||||
fnString: colorForEachRowValidation.toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
propertyName: "textColor",
|
||||
helpText:
|
||||
"Sets the text color of a menu item. Can also be configured the using {{currentItem}} binding.",
|
||||
label: "Text color",
|
||||
controlType: "COLOR_PICKER",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
isJSConvertible: true,
|
||||
customJSControl: "MENU_BUTTON_DYNAMIC_ITEMS",
|
||||
evaluatedDependencies: ["primaryColumns"],
|
||||
validation: {
|
||||
type: ValidationTypes.FUNCTION,
|
||||
params: {
|
||||
regex: /^(?![<|{{]).+/,
|
||||
fnString: colorForEachRowValidation.toString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -13,6 +13,7 @@ import {
|
|||
} from "utils/DynamicBindingUtils";
|
||||
import { createEditActionColumn } from "./utilities";
|
||||
import { PropertyHookUpdates } from "constants/PropertyControlConstants";
|
||||
import { MenuItemsSource } from "widgets/MenuButtonWidget/constants";
|
||||
|
||||
export function totalRecordsCountValidation(
|
||||
value: unknown,
|
||||
|
|
@ -642,6 +643,86 @@ export const updateCustomColumnAliasOnLabelChange = (
|
|||
}
|
||||
};
|
||||
|
||||
export const hideByMenuItemsSource = (
|
||||
props: TableWidgetProps,
|
||||
propertyPath: string,
|
||||
menuItemsSource: MenuItemsSource,
|
||||
) => {
|
||||
const baseProperty = getBasePropertyPath(propertyPath);
|
||||
const currentMenuItemsSource = get(
|
||||
props,
|
||||
`${baseProperty}.menuItemsSource`,
|
||||
"",
|
||||
);
|
||||
|
||||
return currentMenuItemsSource === menuItemsSource;
|
||||
};
|
||||
|
||||
export const hideIfMenuItemsSourceDataIsFalsy = (
|
||||
props: TableWidgetProps,
|
||||
propertyPath: string,
|
||||
) => {
|
||||
const baseProperty = getBasePropertyPath(propertyPath);
|
||||
const sourceData = get(props, `${baseProperty}.sourceData`, "");
|
||||
|
||||
return !sourceData;
|
||||
};
|
||||
|
||||
export const updateMenuItemsSource = (
|
||||
props: TableWidgetProps,
|
||||
propertyPath: string,
|
||||
propertyValue: unknown,
|
||||
): Array<{ propertyPath: string; propertyValue: unknown }> | undefined => {
|
||||
const propertiesToUpdate: Array<{
|
||||
propertyPath: string;
|
||||
propertyValue: unknown;
|
||||
}> = [];
|
||||
const baseProperty = getBasePropertyPath(propertyPath);
|
||||
const menuItemsSource = get(props, `${baseProperty}.menuItemsSource`);
|
||||
|
||||
if (propertyValue === ColumnTypes.MENU_BUTTON && !menuItemsSource) {
|
||||
// Sets the default value for menuItemsSource to static when
|
||||
// selecting the menu button column type for the first time
|
||||
propertiesToUpdate.push({
|
||||
propertyPath: `${baseProperty}.menuItemsSource`,
|
||||
propertyValue: MenuItemsSource.STATIC,
|
||||
});
|
||||
} else {
|
||||
const sourceData = get(props, `${baseProperty}.sourceData`);
|
||||
const configureMenuItems = get(props, `${baseProperty}.configureMenuItems`);
|
||||
const isMenuItemsSourceChangedFromStaticToDynamic =
|
||||
menuItemsSource === MenuItemsSource.STATIC &&
|
||||
propertyValue === MenuItemsSource.DYNAMIC;
|
||||
|
||||
if (isMenuItemsSourceChangedFromStaticToDynamic) {
|
||||
if (!sourceData) {
|
||||
propertiesToUpdate.push({
|
||||
propertyPath: `${baseProperty}.sourceData`,
|
||||
propertyValue: [],
|
||||
});
|
||||
}
|
||||
|
||||
if (!configureMenuItems) {
|
||||
propertiesToUpdate.push({
|
||||
propertyPath: `${baseProperty}.configureMenuItems`,
|
||||
propertyValue: {
|
||||
label: "Configure Menu Items",
|
||||
id: "config",
|
||||
config: {
|
||||
id: "config",
|
||||
label: "Menu Item",
|
||||
isVisible: true,
|
||||
isDisabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return propertiesToUpdate?.length ? propertiesToUpdate : undefined;
|
||||
};
|
||||
|
||||
export function selectColumnOptionsValidation(
|
||||
value: unknown,
|
||||
props: TableWidgetProps,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
getOriginalRowIndex,
|
||||
getSelectRowIndex,
|
||||
getSelectRowIndices,
|
||||
getSourceDataAndCaluclateKeysForEventAutoComplete,
|
||||
getTableStyles,
|
||||
reorderColumns,
|
||||
} from "./utilities";
|
||||
|
|
@ -1992,6 +1993,264 @@ describe("getColumnType", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("getSourceDataAndCaluclateKeysForEventAutoComplete", () => {
|
||||
it("Should test with valid values", () => {
|
||||
const mockProps = {
|
||||
type: "TABLE_WIDGET_V2",
|
||||
widgetName: "Table1",
|
||||
widgetId: "9oh3qyw84m",
|
||||
primaryColumns: {
|
||||
action: {
|
||||
configureMenuItems: {
|
||||
config: {
|
||||
label:
|
||||
"{{Table1.primaryColumns.action.sourceData.map((currentItem, currentIndex) => ( currentItem.))}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
__evaluation__: {
|
||||
errors: {
|
||||
primaryColumns: [],
|
||||
},
|
||||
evaluatedValues: {
|
||||
primaryColumns: {
|
||||
step: {
|
||||
index: 0,
|
||||
width: 150,
|
||||
id: "step",
|
||||
originalId: "step",
|
||||
alias: "step",
|
||||
columnType: "text",
|
||||
label: "step",
|
||||
computedValue: ["#1", "#2", "#3"],
|
||||
validation: {},
|
||||
labelColor: "#FFFFFF",
|
||||
},
|
||||
action: {
|
||||
index: 3,
|
||||
width: 150,
|
||||
id: "action",
|
||||
originalId: "action",
|
||||
alias: "action",
|
||||
columnType: "menuButton",
|
||||
label: "action",
|
||||
computedValue: ["", "", ""],
|
||||
labelColor: "#FFFFFF",
|
||||
buttonLabel: ["Action", "Action", "Action"],
|
||||
menuColor: ["#553DE9", "#553DE9", "#553DE9"],
|
||||
menuItemsSource: "DYNAMIC",
|
||||
menuButtonLabel: ["Open Menu", "Open Menu", "Open Menu"],
|
||||
sourceData: [
|
||||
{
|
||||
gender: "male",
|
||||
name: "Victor",
|
||||
email: "victor.garrett@example.com",
|
||||
},
|
||||
{
|
||||
gender: "male",
|
||||
name: "Tobias",
|
||||
email: "tobias.hansen@example.com",
|
||||
},
|
||||
{
|
||||
gender: "female",
|
||||
name: "Jane",
|
||||
email: "jane.coleman@example.com",
|
||||
},
|
||||
{
|
||||
gender: "female",
|
||||
name: "Yaromira",
|
||||
email: "yaromira.manuylenko@example.com",
|
||||
},
|
||||
{
|
||||
gender: "male",
|
||||
name: "Andre",
|
||||
email: "andre.ortiz@example.com",
|
||||
},
|
||||
],
|
||||
configureMenuItems: {
|
||||
label: "Configure Menu Items",
|
||||
id: "config",
|
||||
config: {
|
||||
id: "config",
|
||||
label: ["male", "male", "female", "female", "male"],
|
||||
isVisible: true,
|
||||
isDisabled: false,
|
||||
onClick: "",
|
||||
backgroundColor: ["red", "red", "tan", "tan", "red"],
|
||||
iconName: "add-row-top",
|
||||
iconAlign: "right",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = getSourceDataAndCaluclateKeysForEventAutoComplete(
|
||||
mockProps as any,
|
||||
);
|
||||
const expected = {
|
||||
currentItem: {
|
||||
name: "",
|
||||
email: "",
|
||||
gender: "",
|
||||
},
|
||||
};
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it("Should test with empty sourceData", () => {
|
||||
const mockProps = {
|
||||
type: "TABLE_WIDGET_V2",
|
||||
widgetName: "Table1",
|
||||
widgetId: "9oh3qyw84m",
|
||||
primaryColumns: {
|
||||
action: {
|
||||
configureMenuItems: {
|
||||
config: {
|
||||
label:
|
||||
"{{Table1.primaryColumns.action.sourceData.map((currentItem, currentIndex) => ( currentItem.))}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
__evaluation__: {
|
||||
errors: {
|
||||
primaryColumns: [],
|
||||
},
|
||||
evaluatedValues: {
|
||||
primaryColumns: {
|
||||
step: {
|
||||
index: 0,
|
||||
width: 150,
|
||||
id: "step",
|
||||
originalId: "step",
|
||||
alias: "step",
|
||||
columnType: "text",
|
||||
label: "step",
|
||||
computedValue: ["#1", "#2", "#3"],
|
||||
validation: {},
|
||||
labelColor: "#FFFFFF",
|
||||
},
|
||||
action: {
|
||||
index: 3,
|
||||
width: 150,
|
||||
id: "action",
|
||||
originalId: "action",
|
||||
alias: "action",
|
||||
columnType: "menuButton",
|
||||
label: "action",
|
||||
computedValue: ["", "", ""],
|
||||
labelColor: "#FFFFFF",
|
||||
buttonLabel: ["Action", "Action", "Action"],
|
||||
menuColor: ["#553DE9", "#553DE9", "#553DE9"],
|
||||
menuItemsSource: "DYNAMIC",
|
||||
menuButtonLabel: ["Open Menu", "Open Menu", "Open Menu"],
|
||||
sourceData: [],
|
||||
configureMenuItems: {
|
||||
label: "Configure Menu Items",
|
||||
id: "config",
|
||||
config: {
|
||||
id: "config",
|
||||
label: ["male", "male", "female", "female", "male"],
|
||||
isVisible: true,
|
||||
isDisabled: false,
|
||||
onClick: "",
|
||||
backgroundColor: ["red", "red", "tan", "tan", "red"],
|
||||
iconName: "add-row-top",
|
||||
iconAlign: "right",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = getSourceDataAndCaluclateKeysForEventAutoComplete(
|
||||
mockProps as any,
|
||||
);
|
||||
const expected = { currentItem: {} };
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it("Should test without sourceData", () => {
|
||||
const mockProps = {
|
||||
type: "TABLE_WIDGET_V2",
|
||||
widgetName: "Table1",
|
||||
widgetId: "9oh3qyw84m",
|
||||
primaryColumns: {
|
||||
action: {
|
||||
configureMenuItems: {
|
||||
config: {
|
||||
label:
|
||||
"{{Table1.primaryColumns.action.sourceData.map((currentItem, currentIndex) => ( currentItem.))}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
__evaluation__: {
|
||||
errors: {
|
||||
primaryColumns: [],
|
||||
},
|
||||
evaluatedValues: {
|
||||
primaryColumns: {
|
||||
step: {
|
||||
index: 0,
|
||||
width: 150,
|
||||
id: "step",
|
||||
originalId: "step",
|
||||
alias: "step",
|
||||
columnType: "text",
|
||||
label: "step",
|
||||
computedValue: ["#1", "#2", "#3"],
|
||||
validation: {},
|
||||
labelColor: "#FFFFFF",
|
||||
},
|
||||
action: {
|
||||
index: 3,
|
||||
width: 150,
|
||||
id: "action",
|
||||
originalId: "action",
|
||||
alias: "action",
|
||||
columnType: "menuButton",
|
||||
label: "action",
|
||||
computedValue: ["", "", ""],
|
||||
labelColor: "#FFFFFF",
|
||||
buttonLabel: ["Action", "Action", "Action"],
|
||||
menuColor: ["#553DE9", "#553DE9", "#553DE9"],
|
||||
menuItemsSource: "DYNAMIC",
|
||||
menuButtonLabel: ["Open Menu", "Open Menu", "Open Menu"],
|
||||
configureMenuItems: {
|
||||
label: "Configure Menu Items",
|
||||
id: "config",
|
||||
config: {
|
||||
id: "config",
|
||||
label: ["male", "male", "female", "female", "male"],
|
||||
isVisible: true,
|
||||
isDisabled: false,
|
||||
onClick: "",
|
||||
backgroundColor: ["red", "red", "tan", "tan", "red"],
|
||||
iconName: "add-row-top",
|
||||
iconAlign: "right",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = getSourceDataAndCaluclateKeysForEventAutoComplete(
|
||||
mockProps as any,
|
||||
);
|
||||
const expected = { currentItem: {} };
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getArrayPropertyValue", () => {
|
||||
it("should test that it returns the same value when value is not of expected type", () => {
|
||||
expect(getArrayPropertyValue("test", 1)).toEqual("test");
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import { ButtonVariantTypes } from "components/constants";
|
|||
import { dateFormatOptions } from "widgets/constants";
|
||||
import moment from "moment";
|
||||
import { Stylesheet } from "entities/AppTheming";
|
||||
import { getKeysFromSourceDataForEventAutocomplete } from "widgets/MenuButtonWidget/widget/helper";
|
||||
|
||||
type TableData = Array<Record<string, unknown>>;
|
||||
|
||||
|
|
@ -234,12 +235,19 @@ export const getPropertyValue = (
|
|||
value: any,
|
||||
index: number,
|
||||
preserveCase = false,
|
||||
isSourceData = false,
|
||||
) => {
|
||||
if (value && isObject(value) && !Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
if (value && Array.isArray(value) && value[index]) {
|
||||
return preserveCase
|
||||
const getValueForSourceData = (value: any, index: number) => {
|
||||
return Array.isArray(value[index]) ? value[index] : value;
|
||||
};
|
||||
|
||||
return isSourceData
|
||||
? getValueForSourceData(value, index)
|
||||
: preserveCase
|
||||
? value[index].toString()
|
||||
: value[index].toString().toUpperCase();
|
||||
} else if (value) {
|
||||
|
|
@ -307,6 +315,18 @@ export const getCellProperties = (
|
|||
rowIndex,
|
||||
true,
|
||||
),
|
||||
menuItemsSource: getPropertyValue(
|
||||
columnProperties.menuItemsSource,
|
||||
rowIndex,
|
||||
true,
|
||||
),
|
||||
sourceData: getPropertyValue(
|
||||
columnProperties.sourceData,
|
||||
rowIndex,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
configureMenuItems: columnProperties.configureMenuItems,
|
||||
buttonVariant: getPropertyValue(
|
||||
columnProperties.buttonVariant,
|
||||
rowIndex,
|
||||
|
|
@ -697,3 +717,22 @@ export const getColumnType = (
|
|||
return ColumnTypes.TEXT;
|
||||
}
|
||||
};
|
||||
|
||||
export const getSourceDataAndCaluclateKeysForEventAutoComplete = (
|
||||
props: TableWidgetProps,
|
||||
): unknown => {
|
||||
const { __evaluation__, primaryColumns } = props;
|
||||
const primaryColumnKeys = primaryColumns ? Object.keys(primaryColumns) : [];
|
||||
const columnName = primaryColumnKeys?.length ? primaryColumnKeys[0] : "";
|
||||
const evaluatedColumns: any = __evaluation__?.evaluatedValues?.primaryColumns;
|
||||
|
||||
if (evaluatedColumns) {
|
||||
const result = getKeysFromSourceDataForEventAutocomplete(
|
||||
evaluatedColumns[columnName]?.sourceData || [],
|
||||
);
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -942,7 +942,7 @@ export const VALIDATORS: Record<ValidationTypes, Validator> = {
|
|||
{},
|
||||
false,
|
||||
undefined,
|
||||
[value, props, _, moment, propertyPath],
|
||||
[value, props, _, moment, propertyPath, config],
|
||||
);
|
||||
return result;
|
||||
} catch (e) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user