PromucFlow_constructor/app/client/src/WidgetProvider/factory/index.tsx
Preet Sidhu cf764a9b40
chore: add configuration for large and small widgets (#28671)
## Description

Use case - Widgets need to be distinguished as large or small in Anvil.

1. Add property ``isLargeWidget`` to ``AnvilConfig`` interface.
2. Update AnvilConfig for all widget. Especially, WDS widgets.

#### Type of change
- Chore (housekeeping or task changes that don't impact user perception)

## Testing

#### How Has This Been Tested?
> Please describe the tests that you ran to verify your changes. Also
list any relevant details for your test configuration.
> Delete anything that is not relevant
- [x] Manual
- [ ] JUnit
- [ ] Jest
- [ ] Cypress


## Checklist:
#### Dev activity
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [x] PR is being merged under a feature flag


#### QA activity:
- [ ] [Speedbreak
features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-)
have been covered
- [ ] Test plan covers all impacted features and [areas of
interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-)
- [ ] Test plan has been peer reviewed by project stakeholders and other
QA members
- [ ] Manually tested functionality on DP
- [ ] We had an implementation alignment call with stakeholders post QA
Round 2
- [ ] Cypress test cases have been added and approved by SDET/manual QA
- [ ] Added `Test Plan Approved` label after Cypress tests were reviewed
- [ ] Added `Test Plan Approved` label after JUnit tests were reviewed

---------

Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com>
2023-11-14 10:55:48 +05:30

578 lines
15 KiB
TypeScript

import type {
PropertyPaneConfig,
PropertyPaneSectionConfig,
} from "constants/PropertyControlConstants";
import type { WidgetProps } from "widgets/BaseWidget";
import type { RenderMode } from "constants/WidgetConstants";
import * as log from "loglevel";
import type {
AnvilConfig,
AutocompletionDefinitions,
AutoLayoutConfig,
CanvasWidgetStructure,
WidgetConfigProps,
WidgetMethods,
} from "WidgetProvider/constants";
import {
addPropertyConfigIds,
addSearchConfigToPanelConfig,
convertFunctionsToString,
enhancePropertyPaneConfig,
generatePropertyPaneSearchConfig,
PropertyPaneConfigTypes,
} from "./helpers";
import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants";
import type BaseWidget from "widgets/BaseWidget";
import { flow } from "lodash";
import { generateReactKey } from "utils/generators";
import {
WidgetFeaturePropertyEnhancements,
WidgetFeatureProps,
} from "../../utils/WidgetFeatures";
import type { RegisteredWidgetFeatures } from "../../utils/WidgetFeatures";
// import { WIDGETS_COUNT } from "widgets";
import type { SetterConfig } from "entities/AppTheming";
import { freeze, memoize } from "./decorators";
import produce from "immer";
import { defaultSizeConfig } from "layoutSystems/anvil/utils/widgetUtils";
type WidgetDerivedPropertyType = any;
export type DerivedPropertiesMap = Record<string, string>;
export type WidgetType = (typeof WidgetFactory.widgetTypes)[number];
class WidgetFactory {
static widgetTypes: Record<string, string> = {};
static widgetConfigMap: Map<
WidgetType,
Partial<WidgetProps> & WidgetConfigProps & { type: string }
> = new Map();
static widgetsMap: Map<WidgetType, typeof BaseWidget> = new Map();
static widgetBuilderMap: Map<WidgetType, any> = new Map();
static initialize(
widgets: [
typeof BaseWidget,
(widgetProps: CanvasWidgetStructure) => React.ReactNode,
][],
) {
const start = performance.now();
for (const [widget, builder] of widgets) {
WidgetFactory.widgetsMap.set(widget.type, widget);
WidgetFactory.widgetTypes[widget.type] = widget.type;
WidgetFactory.widgetBuilderMap.set(widget.type, builder);
WidgetFactory.configureWidget(widget);
}
log.debug("Widget registration took: ", performance.now() - start, "ms");
}
private static configureWidget(widget: typeof BaseWidget) {
const config = widget.getConfig();
const features = widget.getFeatures();
let enhancedFeatures: Record<string, unknown> = {};
if (features) {
Object.keys(features).forEach((registeredFeature: string) => {
enhancedFeatures = Object.assign(
{},
WidgetFeatureProps[registeredFeature as RegisteredWidgetFeatures],
WidgetFeaturePropertyEnhancements[
registeredFeature as RegisteredWidgetFeatures
](widget),
);
});
}
const _config = {
type: widget.type,
...widget.getDefaults(),
...enhancedFeatures,
searchTags: config.searchTags,
tags: config.tags,
hideCard: !!config.hideCard || !config.iconSVG,
isDeprecated: !!config.isDeprecated,
replacement: config.replacement,
displayName: config.name,
key: generateReactKey(),
iconSVG: config.iconSVG,
isCanvas: config.isCanvas,
needsHeightForContent: config.needsHeightForContent,
};
WidgetFactory.widgetConfigMap.set(widget.type, Object.freeze(_config));
}
static get(type: WidgetType) {
const widget = WidgetFactory.widgetsMap.get(type);
if (widget) {
return widget;
} else {
log.error(`Widget is not defined with type: ${type}`);
return;
}
}
static getConfig(type: WidgetType) {
const config = WidgetFactory.widgetConfigMap.get(type);
if (config) {
return config;
} else {
log.error(`Widget config is not registered for type: ${type}`);
return;
}
}
static getConfigs = () => {
return Object.fromEntries(WidgetFactory.widgetConfigMap);
};
static createWidget(
widgetData: CanvasWidgetStructure,
renderMode: RenderMode,
): React.ReactNode {
const { type } = widgetData;
const builder = WidgetFactory.widgetBuilderMap.get(type);
if (builder) {
const widgetProps = {
key: widgetData.widgetId,
isVisible: true,
...widgetData,
renderMode,
};
return builder(widgetProps);
} else {
const ex: WidgetCreationException = {
message:
"Widget Builder not registered for widget type" + widgetData.type,
};
log.error(ex);
return null;
}
}
@memoize
@freeze
static getWidgetTypes(): WidgetType[] {
return Array.from(WidgetFactory.widgetsMap.keys());
}
@memoize
@freeze
static getWidgetDerivedPropertiesMap(
widgetType: WidgetType,
): DerivedPropertiesMap {
const widget = WidgetFactory.widgetsMap.get(widgetType);
const derivedProperties = widget?.getDerivedPropertiesMap();
if (derivedProperties) {
return derivedProperties;
} else {
log.error(
`Derived properties are not defined for widget type: ${widgetType}`,
);
return {};
}
}
@memoize
@freeze
static getWidgetDefaultPropertiesMap(
widgetType: WidgetType,
): Record<string, string> {
const widget = WidgetFactory.widgetsMap.get(widgetType);
const defaultProperties = widget?.getDefaultPropertiesMap();
if (defaultProperties) {
return defaultProperties;
} else {
log.error(
`Default properties are not defined for widget type: ${widgetType}`,
);
return {};
}
}
@memoize
@freeze
static getWidgetDependencyMap(
widgetType: WidgetType,
): Record<string, string[]> {
const widget = WidgetFactory.widgetsMap.get(widgetType);
const dependencyMap = widget?.getDependencyMap();
if (dependencyMap) {
return dependencyMap;
} else {
log.error(`Dependency map is defined for widget type: ${widgetType}`);
return {};
}
}
@memoize
@freeze
static getWidgetMetaPropertiesMap(
widgetType: WidgetType,
): Record<string, unknown> {
const widget = WidgetFactory.widgetsMap.get(widgetType);
const metaProperties = widget?.getMetaPropertiesMap();
if (metaProperties) {
return metaProperties;
} else {
log.error(
`Meta properties are not defined for widget type: ${widgetType}`,
);
return {};
}
}
@memoize
@freeze
static getWidgetPropertyPaneCombinedConfig(
type: WidgetType,
widgetProperties: WidgetProps,
): readonly PropertyPaneConfig[] {
const contentConfig = WidgetFactory.getWidgetPropertyPaneContentConfig(
type,
widgetProperties,
);
const styleConfig = WidgetFactory.getWidgetPropertyPaneStyleConfig(type);
return [...contentConfig, ...styleConfig];
}
@memoize
@freeze
private static getWidgetPropertyPaneConfigWithMemo(type: WidgetType) {
const widget = WidgetFactory.widgetsMap.get(type);
const propertyPaneConfig = widget?.getPropertyPaneConfig();
const features = widget?.getFeatures();
if (Array.isArray(propertyPaneConfig) && propertyPaneConfig.length > 0) {
const enhance = flow([
enhancePropertyPaneConfig,
convertFunctionsToString,
addPropertyConfigIds,
]);
const enhancedPropertyPaneConfig = enhance(propertyPaneConfig, features);
return enhancedPropertyPaneConfig;
}
}
@memoize
static getWidgetPropertyPaneConfig(
type: WidgetType,
widgetProperties: WidgetProps,
): readonly PropertyPaneConfig[] {
const propertyPaneConfig =
WidgetFactory.getWidgetPropertyPaneConfigWithMemo(type);
if (Array.isArray(propertyPaneConfig) && propertyPaneConfig.length > 0) {
return propertyPaneConfig;
} else {
const config = WidgetFactory.getWidgetPropertyPaneCombinedConfig(
type,
widgetProperties,
);
if (config === undefined) {
log.error("Widget property pane config not defined", type);
return [];
} else {
return config;
}
}
}
@memoize
private static getWidgetPropertyPaneContentConfigWithDynamicPropertyGenerator(
type: WidgetType,
) {
const widget = WidgetFactory.widgetsMap.get(type);
const propertyPaneContentConfig = widget?.getPropertyPaneContentConfig();
const features = widget?.getFeatures();
if (propertyPaneContentConfig) {
const enhance = flow([
enhancePropertyPaneConfig,
convertFunctionsToString,
addPropertyConfigIds,
addSearchConfigToPanelConfig,
]);
const enhancedPropertyPaneContentConfig = enhance(
propertyPaneContentConfig,
features,
PropertyPaneConfigTypes.CONTENT,
type,
);
return enhancedPropertyPaneContentConfig;
} else {
return [];
}
}
@memoize
@freeze
static getWidgetPropertyPaneContentConfig(
type: WidgetType,
widgetProperties: WidgetProps,
): readonly PropertyPaneConfig[] {
const propertyPaneContentConfigWithDynamicPropertyGenerator: PropertyPaneSectionConfig[] =
WidgetFactory.getWidgetPropertyPaneContentConfigWithDynamicPropertyGenerator(
type,
);
if (
propertyPaneContentConfigWithDynamicPropertyGenerator.some(
(d) => d.hasDynamicProperties,
)
) {
return propertyPaneContentConfigWithDynamicPropertyGenerator.map(
(section: PropertyPaneSectionConfig) => {
if (section.hasDynamicProperties) {
const dynamicProperties =
section.generateDynamicProperties?.(widgetProperties);
if (dynamicProperties && dynamicProperties.length) {
addPropertyConfigIds(dynamicProperties, false);
section = produce(section, (draft) => {
draft.children = [...dynamicProperties, ...section.children];
});
}
}
return section;
},
);
} else {
return propertyPaneContentConfigWithDynamicPropertyGenerator;
}
}
@memoize
@freeze
static getWidgetPropertyPaneStyleConfig(
type: WidgetType,
): readonly PropertyPaneConfig[] {
const widget = WidgetFactory.widgetsMap.get(type);
const propertyPaneStyleConfig = widget?.getPropertyPaneStyleConfig();
const features = widget?.getFeatures();
if (propertyPaneStyleConfig) {
const enhance = flow([
enhancePropertyPaneConfig,
convertFunctionsToString,
addPropertyConfigIds,
addSearchConfigToPanelConfig,
]);
const enhancedPropertyPaneConfig = enhance(
propertyPaneStyleConfig,
features,
PropertyPaneConfigTypes.STYLE,
);
return enhancedPropertyPaneConfig;
} else {
return [];
}
}
@memoize
@freeze
static getWidgetPropertyPaneSearchConfig(
type: WidgetType,
widgetProperties: WidgetProps,
): readonly PropertyPaneConfig[] {
const config = generatePropertyPaneSearchConfig(
WidgetFactory.getWidgetPropertyPaneContentConfig(type, widgetProperties),
WidgetFactory.getWidgetPropertyPaneStyleConfig(type),
);
if (config) {
return config;
} else {
return [];
}
}
@memoize
@freeze
static getWidgetAutoLayoutConfig(type: WidgetType): AutoLayoutConfig {
const widget = WidgetFactory.widgetsMap.get(type);
const baseAutoLayoutConfig = widget?.getAutoLayoutConfig();
if (baseAutoLayoutConfig) {
return {
...baseAutoLayoutConfig,
widgetSize:
baseAutoLayoutConfig.widgetSize?.map((sizeConfig) => ({
...sizeConfig,
configuration: (props: WidgetProps) => {
if (!props)
return {
minWidth:
WidgetFactory.widgetConfigMap.get(type)?.minWidth ||
FILL_WIDGET_MIN_WIDTH,
minHeight:
WidgetFactory.widgetConfigMap.get(type)?.minHeight || 80,
};
return sizeConfig.configuration(props);
},
})) || [],
autoDimension: baseAutoLayoutConfig.autoDimension ?? {},
disabledPropsDefaults: baseAutoLayoutConfig.disabledPropsDefaults ?? {},
};
} else {
log.error(`Auto layout config is not defined for widget type: ${type}`);
return {
autoDimension: {},
widgetSize: [],
disableResizeHandles: {},
disabledPropsDefaults: {},
};
}
}
@memoize
@freeze
static getWidgetAnvilConfig(type: WidgetType): AnvilConfig {
const widget = WidgetFactory.widgetsMap.get(type);
const baseAnvilConfig: AnvilConfig | null | undefined =
widget?.getAnvilConfig();
if (!baseAnvilConfig) {
log.error(`Anvil config is not defined for widget type: ${type}`);
return {
isLargeWidget: false,
widgetSize: defaultSizeConfig,
};
}
return baseAnvilConfig;
}
@memoize
@freeze
static getWidgetTypeConfigMap(): WidgetTypeConfigMap {
const typeConfigMap: WidgetTypeConfigMap = {};
WidgetFactory.getWidgetTypes().forEach((type) => {
typeConfigMap[type] = {
defaultProperties: WidgetFactory.getWidgetDefaultPropertiesMap(type),
derivedProperties: WidgetFactory.getWidgetDerivedPropertiesMap(type),
metaProperties: WidgetFactory.getWidgetMetaPropertiesMap(type),
};
});
return typeConfigMap;
}
static getAutocompleteDefinitions(
type: WidgetType,
): AutocompletionDefinitions {
const widget = WidgetFactory.widgetsMap.get(type);
const autocompleteDefinition = widget?.getAutocompleteDefinitions();
if (autocompleteDefinition) {
return autocompleteDefinition;
} else {
log.error(
`Auto complete definitions are not defined for widget type: ${type}`,
);
return {};
}
}
@memoize
@freeze
static getWidgetSetterConfig(type: WidgetType): Partial<SetterConfig> {
const widget = WidgetFactory.widgetsMap.get(type);
const setterConfig = widget?.getSetterConfig() || {};
return setterConfig;
}
@memoize
@freeze
static getLoadingProperties(type: WidgetType): Array<RegExp> | undefined {
const widget = WidgetFactory.widgetsMap.get(type);
return widget?.getLoadingProperties();
}
@memoize
@freeze
static getWidgetStylesheetConfigMap(widgetType: WidgetType) {
const widget = WidgetFactory.widgetsMap.get(widgetType);
const stylesheet = widget?.getStylesheetConfig();
if (stylesheet) {
return stylesheet;
} else {
log.error(
`stylesheet config is not defined for widget type: ${widgetType}`,
);
return undefined;
}
}
@memoize
static getWidgetMethods(type: WidgetType): WidgetMethods {
const widget = WidgetFactory.widgetsMap.get(type);
const methods = widget?.getMethods();
if (methods) {
return methods;
} else {
return {};
}
}
}
export type WidgetTypeConfigMap = Record<
string,
{
defaultProperties: Record<string, string>;
metaProperties: Record<string, any>;
derivedProperties: WidgetDerivedPropertyType;
}
>;
export interface WidgetCreationException {
message: string;
}
export default WidgetFactory;