Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 11 additions & 23 deletions scripts/dxcomponents/components-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,34 +30,22 @@ export class ComponentsManager {

/**
* Creates a component.
*
* Warning: We cannot init() component here because it may cause infinite loop.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, you should be able to create & init components in all places where you are operating on local variables.

* If we init() child before adding it to parent the following flow may occur:
* 1. Parent creates child (but does not add it to its children list yet)
* 2. Child init() is called
* 3. Parent is updated (because of redux store update)
* 4. Parent creates child again because its children list is empty.
* 5. Child init() is called again - and loops occurs.
*
* @param type - type of component to create
* @param args - arguments to pass to the component's constructor
* @param init - if true, calls the component's init() method after creation
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no longer valid

* @returns the created component
*/
create(type, args = [], init = true) {
create(type, args = []) {
const ComponentClass = getComponentFromMap(type);
const component = new ComponentClass(this, ...args);
if (init) {
component.init();
}
return component;
}

/**
* Creates or updates a component.
* @param component - component to update, or null/undefined to create a new one
* @param type - type of component to create
* @param args - arguments to pass to the component's constructor
* @param init - if true, calls the component's init() method after creation
* @returns created or updated component
*/
upsert(component, type, args = [], init = true) {
if (component) {
component.update(...args);
return component;
} else {
return this.create(type, args, init);
}
return new ComponentClass(this, ...args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class AssignmentCardComponent extends ContainerBaseComponent {
this.arSecondaryButtons$,
this.actionButtonClick,
]);
this.actionButtonsComponent.init();
this.sendPropsUpdate();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,12 @@ export class AssignmentComponent extends BaseComponent {
this.arSecondaryButtons$,
this.onActionButtonClick,
];
this.assignmentCardComponent = this.componentsManager.upsert(
this.assignmentCardComponent,
"AssignmentCard",
assignmentCardArgs
);
if (this.assignmentCardComponent) {
this.assignmentCardComponent.update(...assignmentCardArgs);
} else {
this.assignmentCardComponent = this.componentsManager.create("AssignmentCard", assignmentCardArgs);
this.assignmentCardComponent.init();
}
this.loading = this.newPConn.getLoadingStatus();
this.sendPropsUpdate();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class ContainerBaseComponent extends BaseComponent {
oldChildrenComponents.splice(oldChildrenComponents.indexOf(oldComponentToReuse), 1);
} else {
const newPConn = newChild.getPConnect();
const newChildComponent = this.componentsManager.create(newPConn.meta.type, [newPConn], false);
const newChildComponent = this.componentsManager.create(newPConn.meta.type, [newPConn]);
reconciledComponents.push(newChildComponent);
uninitializedComponents.push(newChildComponent);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,11 @@ export class FlowContainerComponent extends BaseComponent {
]
: [];

this.alertBannerComponents = banners.map((b) =>
this.componentsManager.create("AlertBanner", [b.variant, b.messages], true)
);
this.alertBannerComponents = banners.map((b) => {
const component = this.componentsManager.create("AlertBanner", [b.variant, b.messages]);
component.init();
return component;
});
}

#destroyBanners() {
Expand Down Expand Up @@ -171,6 +173,7 @@ export class FlowContainerComponent extends BaseComponent {
this.childrenPConns,
this.containerContextKey,
]);
this.assignmentComponent.init();
}

#updateSelf() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,11 @@ export class ModalViewContainerComponent extends ContainerBaseComponent {
]
: [];

this.alertBannerComponents = banners.map((b) =>
this.componentsManager.create("AlertBanner", [b.variant, b.messages], true)
);
this.alertBannerComponents = banners.map((b) => {
const component = this.componentsManager.create("AlertBanner", [b.variant, b.messages]);
component.init();
return component;
});
}

#getBannerMessages() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,12 @@ export class RootContainerComponent extends BaseComponent {
options,
};
const viewContainerPConn = PCore.createPConnect(viewContConfig).getPConnect();
this.#viewContainerComponent = this.componentsManager.upsert(
this.#viewContainerComponent,
viewContainerPConn.meta.type,
[viewContainerPConn]
);
if (this.#viewContainerComponent) {
this.#viewContainerComponent.update(viewContainerPConn)
} else {
this.#viewContainerComponent = this.componentsManager.create(viewContainerPConn.meta.type, [viewContainerPConn])
this.#viewContainerComponent.init();
}
}

#configureModalContainer() {
Expand All @@ -122,11 +123,12 @@ export class RootContainerComponent extends BaseComponent {
});

const modalViewContainerPConn = configObjModal.getPConnect();
this.#modalViewContainerComponent = this.componentsManager.upsert(
this.#modalViewContainerComponent,
modalViewContainerPConn.meta.type,
[modalViewContainerPConn]
);
if (this.#modalViewContainerComponent) {
this.#modalViewContainerComponent.update(modalViewContainerPConn)
} else {
this.#modalViewContainerComponent = this.componentsManager.create(modalViewContainerPConn.meta.type, [modalViewContainerPConn])
this.#modalViewContainerComponent.init();
}
if (this.compId !== "1") {
console.error(TAG, "RootComponent id must be '1' to match root container on consumer side");
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,14 @@ export class FieldGroupTemplateComponent extends BaseComponent {
lookForChildInConfig,
evaluateAllowRowAction(allowRowEdit, item)
).getPConnect();
const newComponent = this.componentsManager.upsert(oldComponent, newPConn.meta.type, [newPConn]);
let newComponent;
if (oldComponent) {
oldComponent.update(newPConn)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon in all places where upsert has been inlined.

newComponent = oldComponent;
} else {
newComponent = this.componentsManager.create(newPConn.meta.type, [newPConn]);
newComponent.init();
}
updatedItems.push({
id: index,
name:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,14 @@ export class SimpleTableManualComponent extends BaseComponent {
};
const cellPConn = PCore.createPConnect(config).getPConnect();
const oldComponent = editableRow?.cells?.[cellIndex]?.component;
const newComponent = this.componentsManager.upsert(oldComponent, cellPConn.meta.type, [cellPConn]);
let newComponent;
if (oldComponent) {
oldComponent.update(cellPConn)
newComponent = oldComponent
} else {
newComponent = this.componentsManager.create(cellPConn.meta.type, [cellPConn])
newComponent.init();
}
newEditableCells.push({ component: newComponent })
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,22 @@ export class SimpleTableComponent extends BaseComponent {
}
if (multiRecordDisplayAs === "fieldGroup") {
const fieldGroupProps = { ...configProps, contextClass };
this.childComponent = this.componentsManager.upsert(this.childComponent, "FieldGroupTemplate", [
this.pConn,
fieldGroupProps,
]);
if (this.childComponent) {
this.childComponent.update(this.pConn, fieldGroupProps)
} else {
this.childComponent = this.componentsManager.create("FieldGroupTemplate", [this.pConn, fieldGroupProps]);
this.childComponent.init();
}
this.#sendPropsUpdate();
} else if (fieldMetadata && fieldMetadata.type === 'Page List' && fieldMetadata.dataRetrievalType === 'refer') {
console.warn(TAG, 'Displaying ListView in SimpleTable is not supported yet.');
} else {
this.childComponent = this.componentsManager.upsert(this.childComponent, "SimpleTableManual", [this.pConn]);
if (this.childComponent) {
this.childComponent.update(this.pConn);
} else {
this.childComponent = this.componentsManager.create("SimpleTableManual", [this.pConn]);
this.childComponent.init();
}
this.#sendPropsUpdate();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export class ViewContainerComponent extends BaseComponent {
const viewPConn = ReferenceComponent.normalizePConn(newCompPConn);
this.childComponent?.destroy?.();
this.childComponent = this.componentsManager.create(viewPConn.meta.type, [viewPConn]);
this.childComponent.init();
this.props.children = [this.childComponent.compId];
this.componentsManager.onComponentPropsUpdate(this);
}
Expand Down
52 changes: 50 additions & 2 deletions scripts/dxcomponents/components/containers/view.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,30 @@ export class ViewComponent extends ContainerBaseComponent {
this.destroyChildren();
}

this.childrenComponents[0] = this.componentsManager.upsert(this.childrenComponents[0], template, [this.pConn]);
if (this.childrenComponents[0]) {
this.childrenComponents[0].update(this.pConn);
} else {
this.childrenComponents[0] = this.componentsManager.create(template, [this.pConn]);
this.childrenComponents[0].init();
}
}

#evaluateVisibility(pConn, referenceContext) {
const visibilityExpression = pConn.meta.config.visibility;
if (!visibilityExpression || visibilityExpression.length === 0) return true;
const contextName = pConn.getContextName();
if (visibilityExpression.startsWith("@E ")) {
return this.#evaluateExpressionVisibility(contextName, referenceContext, visibilityExpression);
} else if(visibilityExpression.startsWith("@W ")) {
return this.#evaluateWhenVisibility(contextName, visibilityExpression);
} else {
console.warn(TAG, `Unsupported visibility expression: ${visibilityExpression}. Defaulting to visible.`);
return true;
}
}

let dataPage = this.#getDataPage(pConn.getContextName(), referenceContext);
#evaluateExpressionVisibility(contextName, referenceContext, visibilityExpression) {
let dataPage = this.#getDataPage(contextName, referenceContext);
if (!dataPage) return false;

const visibilityConditions = visibilityExpression.replace("@E ", "");
Expand All @@ -136,6 +152,38 @@ export class ViewComponent extends ContainerBaseComponent {
});
}

#evaluateWhenVisibility(contextName, visibilityExpression) {
let bVisibility = true;
// e.g. "@E .EmbeddedData_SelectedTestName == 'Readonly' && .EmbeddedData_SelectedSubCategory == 'Mode'"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're evaluating @W here, not @E.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implementation was changes so that in case od @W configProps.visibility is used

const aVisibility = visibilityExpression.split('&&');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we supporting && only? What about other operators?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code removed

// e.g. ["EmbeddedData_SelectedTestName": "Readonly", "EmbeddedData_SelectedSubCategory": "Mode"]
// Reading values from the Store to evaluate the visibility expressions
const storeData = PCore.getStore().getState()?.data[contextName].caseInfo.content;

const initialVal = {};
const oProperties = aVisibility.reduce((properties, property) => {
const keyStartIndex = property.indexOf('.');
const keyEndIndex = property.indexOf('=') - 1;
const valueStartIndex = property.indexOf("'");
const valueEndIndex = property.lastIndexOf("'") - 1;
return {
...properties,
[property.substr(keyStartIndex + 1, keyEndIndex - keyStartIndex - 1)]: property.substr(valueStartIndex + 1, valueEndIndex - valueStartIndex)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a piece of custom code for expression evaluation. Can't you use pcore/pconnect APIs here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code removed

};
}, initialVal);

const propertyKeys = Object.keys(oProperties);
const propertyValues = Object.values(oProperties);

for (let propertyIndex = 0; propertyIndex < propertyKeys.length; propertyIndex++) {
if (storeData[propertyKeys[propertyIndex]] !== propertyValues[propertyIndex]) {
bVisibility = false;
}
}

return bVisibility;
}

#getDataPage(context, referenceContext) {
let pageReferenceKeys = referenceContext.replace("caseInfo.content.", "").split(".");
let page = PCore.getStore().getState()?.data[context].caseInfo.content;
Expand Down
4 changes: 3 additions & 1 deletion scripts/init/initial-render.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ export function initialRender(renderObj) {
(component) => bridge.updateNativeComponent(component)
);

return componentsManager.create("RootContainer", [pconn]);
const root = componentsManager.create("RootContainer", [pconn]);
root.init();
return root;
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ fun EditableTable(
) {
Column {
Heading(label, Modifier.padding(vertical = 8.dp), fontSize = 16.sp)

if (columns.isEmpty()) return
val focusManager = LocalFocusManager.current
val haveEditColumn = rows.any { it.onEditButtonClick != null }
val haveDeleteColumn = rows.any { it.onDeleteButtonClick != null }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.pega.constellation.sdk.kmp.ui.components.cmp.controls.form

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.OutlinedTextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.TextFieldValue
import be.digitalia.compose.htmlconverter.htmlToAnnotatedString
import com.pega.constellation.sdk.kmp.ui.components.cmp.controls.form.internal.Input
import com.pega.constellation.sdk.kmp.ui.components.cmp.controls.form.internal.HelperText

@Composable
fun RichText(
Expand All @@ -22,17 +25,35 @@ fun RichText(
// Read-only RichText renders on web as display-only
RichTextFieldValue(label, value)
} else {
Input(
value = TextFieldValue(rememberAnnotated(value)),
label = label,
modifier = modifier,
helperText = helperText,
validateMessage = validateMessage,
required = required,
disabled = disabled,
readOnly = true, // not allowing editing in RichText for now
lines = 3
)
Column(modifier = modifier) {
// not allowing editing in RichText for now
OutlinedTextField(
value = TextFieldValue(rememberAnnotated(value)),
onValueChange = {},
modifier = Modifier.fillMaxWidth(),
label = {
Label(
label = label,
hideLabel = false,
required = required,
disabled = disabled,
readOnly = true
)
},
placeholder = { },
enabled = !disabled,
readOnly = true,
singleLine = false,
minLines = 3,
maxLines = 3,
)
HelperText(
text = helperText,
validateMessage = validateMessage,
disabled = disabled,
readOnly = true
)
}
}
}

Expand Down
Loading