diff --git a/docs-site/src/pages/pattern-lab/_patterns/06-experiments/editor/25-editor-with-two-character-layout.twig b/docs-site/src/pages/pattern-lab/_patterns/06-experiments/editor/25-editor-with-two-character-layout.twig
new file mode 100644
index 0000000000..1213b2a3bc
--- /dev/null
+++ b/docs-site/src/pages/pattern-lab/_patterns/06-experiments/editor/25-editor-with-two-character-layout.twig
@@ -0,0 +1,14 @@
+{% set content %}
+
+ How Pega technology resolves
+
+
+ {% include "./billing-micro-journey-fragments/_step-one.twig" %}
+
+
+
+{% endset %}
+
+{% include '@bolt-components-editor/editor-in-pl.twig' with {
+ content: content,
+} only %}
diff --git a/packages/micro-journeys/src/character.scss b/packages/micro-journeys/src/character.scss
index e75e3c8408..7fa2b6ec17 100644
--- a/packages/micro-journeys/src/character.scss
+++ b/packages/micro-journeys/src/character.scss
@@ -9,6 +9,10 @@ bolt-svg-animations {
width: 100%;
}
+bolt-animate[slot="connection"] {
+ display: flex;
+}
+
.c-bolt-character {
display: flex;
flex-wrap: wrap;
diff --git a/packages/micro-journeys/src/two-character-layout.js b/packages/micro-journeys/src/two-character-layout.js
index 345ac3b716..2742f4faca 100644
--- a/packages/micro-journeys/src/two-character-layout.js
+++ b/packages/micro-journeys/src/two-character-layout.js
@@ -7,7 +7,10 @@ import {
} from '@bolt/micro-journeys/src/character';
import { boltConnectionIs } from '@bolt/micro-journeys/src/connection';
import { triggerAnims } from '@bolt/components-animate/utils';
-import { equalizeRelativeHeights } from './utils/equalize-relative-heights';
+import {
+ equalizeRelativeHeights,
+ equalizeRelativeHeightsClass,
+} from './utils/equalize-relative-heights';
import styles from './two-character-layout.scss';
let cx = classNames.bind(styles);
@@ -23,6 +26,7 @@ class BoltTwoCharacterLayout extends withLitHtml() {
...props.boolean,
...{ default: false },
},
+ // Has the parent `bolt-interactive-step` attempted its animation of its children in?
parentAnimationsTriggered: {
...props.boolean,
...{ default: false },
@@ -33,78 +37,19 @@ class BoltTwoCharacterLayout extends withLitHtml() {
constructor(self) {
self = super(self);
self.useShadow = hasNativeShadowDomSupport;
- self.hasConnection = !!this.querySelector(boltConnectionIs);
- self.requiredComponentCount = self.hasConnection ? 3 : 2;
- self.renderedComponentCount = 0;
+ self.connection = this.querySelector(boltConnectionIs);
+ self.characters = [...this.querySelectorAll(boltCharacterIs)];
self.isInitialRender = true;
return self;
}
- /**
- * Callback for when child components report ready. Figure out if they're
- * characters and count them if so. Then attempt to initialize.
- *
- * @param event
- */
- handleChildrenReady(event) {
- if (
- event.detail.name !== boltCharacterIs &&
- event.detail.name !== boltConnectionIs
- ) {
- return;
- }
- this.renderedComponentCount++;
- if (this.isConnected) {
- this.attemptCharactersAreReadyInitialization();
- }
- }
-
- /**
- * Make sure that the component is rendering for the first time and `bolt-character`s are ready.
- * Both characters have to be on the dom with width and height before this can run.
- * The problem is that this component connects and components report as rendered
- * before content is slotted. Thus, we have to wait for the parent to tell us
- * that it has attempted to animate content as a sigh that we can actually
- * perform calculations against slotted elements that have height/width.
- * We only do this on initial render after we're told the parent has animated.
- * After this, bolt-interactive-step is in charge of animating this in and out.
- */
- attemptCharactersAreReadyInitialization() {
- const charsAreReady =
- this.renderedComponentCount >= this.requiredComponentCount;
- if (
- this.parentAnimationsTriggered &&
- charsAreReady &&
- this.isInitialRender
- ) {
- setTimeout(() => {
- window.requestAnimationFrame(() => {
- this.charactersAreReadyInitialization();
- this.isInitialRender = false;
- });
- });
- }
- }
-
- connected() {
- this.addEventListener('ready', this.handleChildrenReady);
- }
-
- disconnecting() {
- if (this.parentAnimationsTriggered && !this.isInitialRender) {
- this.removeEventListener('ready', this.handleChildrenReady);
- }
- }
-
/**
* Make sure both pathway main images are at exactly the same height relative
* to one another so a connection can be centered between them, while
* preserving document flow by avoiding absolute positioning.
*/
- charactersAreReadyInitialization = async () => {
- const anims = this.querySelectorAll('bolt-animate');
- this.boltCharacters = [...this.querySelectorAll(boltCharacterIs)];
- const eqHeightArgs = this.boltCharacters.map(character => {
+ equalizeCharactersAndStyleConnection = () => {
+ const eqHeightArgs = this.characters.map(character => {
return {
container: character,
elToEqualize: character.renderRoot.querySelector(
@@ -113,12 +58,16 @@ class BoltTwoCharacterLayout extends withLitHtml() {
paddingEqualizationTarget: character,
};
});
+
equalizeRelativeHeights(
eqHeightArgs,
- this.hasConnection ? this.setConnectionWidth : null,
+ this.connection ? this.setConnectionWidth : null,
);
this.triggerUpdate();
+ };
+ animateContentIn = () => {
+ const anims = this.querySelectorAll('bolt-animate');
triggerAnims({ animEls: anims, stage: 'IN' });
// Tell the parent step that it can now normally animate this element.
this.dispatchEvent(
@@ -126,6 +75,7 @@ class BoltTwoCharacterLayout extends withLitHtml() {
bubbles: true,
}),
);
+ this.isInitialRender = false;
};
/**
@@ -133,9 +83,9 @@ class BoltTwoCharacterLayout extends withLitHtml() {
* `bolt-character`. Note: requires page refresh.
*/
setConnectionWidth = () => {
- this.boltCharacters.forEach((e, i) => {
- const connection = e.querySelector(boltConnectionIs);
- const nextCharacter = this.boltCharacters[i + 1];
+ this.characters.forEach((e, i) => {
+ const connection = e.querySelector('bolt-animate[slot="connection"');
+ const nextCharacter = this.characters[i + 1];
if (connection && nextCharacter) {
const nextCharacterCenter = nextCharacter.renderRoot.querySelector(
`.${boltCharacterCenterClass}`,
@@ -144,14 +94,89 @@ class BoltTwoCharacterLayout extends withLitHtml() {
.left - connection.getBoundingClientRect().left}px + 50%)`;
connection.renderRoot.querySelector('.c-bolt-connection__main-image');
}
+ // Hack for chrome which gets confused by [slot='connection']
+ // e.querySelector('bolt-animate[slot="connection"').style.display = 'flex';
+ });
+ };
+
+ /**
+ * Create a delay and return a promise.
+ *
+ * @param {int} timeoutAmount in MS
+ * @return {Promise}
+ */
+ delay = async timeoutAmount =>
+ new Promise(resolve => {
+ setTimeout(resolve, timeoutAmount);
+ });
+
+ /**
+ * Ensure with recursive promises that the `bolt-character`s and `bolt-connection`
+ * are rendered. This is necessary because bolt element ready
+ * event fires after JS render but before render to the actual DOM.
+ *
+ * @param attemptCount
+ * @return {Promise}
+ */
+ ensureComponentsRendered = async (attemptCount = 0) => {
+ const attemptMax = 15;
+ const attemptTimeout = 900;
+ return new Promise((resolve, reject) => {
+ if (attemptMax <= attemptCount) {
+ return reject(
+ new Error(
+ "Uh oh. Characters didn't render to the dom in a timely fashion.",
+ ),
+ );
+ }
+ if (this.areComponentsRendered()) {
+ return resolve(true);
+ } else {
+ // I realize this seems primitive, but it is the best way.
+ this.delay(attemptTimeout).then(() => {
+ return this.ensureComponentsRendered(attemptCount + 1);
+ });
+ }
+ });
+ };
+
+ /**
+ * Check the `bolt-character`s and `bolt-connection` to see if they have
+ * offsetHeight, that is, if they are painted in the browser yet.
+ *
+ * @return {boolean} if all required components have offsetHeight.
+ */
+ areComponentsRendered = () => {
+ this.characters.forEach(character => {
+ if (!character.offsetHeight) {
+ return false;
+ }
+ });
+ if (this.connection) {
+ if (!this.connection.offsetHeight) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ /**
+ * Check to see if `bolt-character`s have had their heights equalized.
+ *
+ * @return {boolean} true if equalized else false.
+ */
+ areCharactersEqualized = () => {
+ let isEqualized = true;
+ this.characters.forEach(character => {
+ if (!character.classList.contains(equalizeRelativeHeightsClass)) {
+ isEqualized = false;
+ }
});
+ return isEqualized;
};
render() {
const props = this.validateProps(this.props);
- if (this.isInitialRender) {
- this.attemptCharactersAreReadyInitialization();
- }
const classes = cx('c-bolt-two-character-layout', {
'c-bolt-two-character-layout__initial':
!props.parentAnimationsTriggered && this.isInitialRender,
@@ -174,6 +199,37 @@ class BoltTwoCharacterLayout extends withLitHtml() {
`;
}
+
+ /**
+ * Make sure that the component is rendering for the first time and `bolt-character`s are ready.
+ * Both characters have to be on the dom with width and height before this can run.
+ * The problem is that this component connects and components report as rendered
+ * before content is slotted. Thus, we have to wait for the parent to tell us
+ * that it has attempted to animate content as a sign that we can actually
+ * perform calculations against slotted elements that have height/width.
+ * We only do this on initial render after we're told the parent has animated.
+ * After this, `bolt-interactive-step` is in charge of animating `this` in and out.
+ */
+ rendered() {
+ super.rendered();
+ // The editor disconnects the old component and copies the HTML to an iframe.
+ // Don't perform animations or calculations on an unconnected component.
+ if (!this.isConnected) {
+ return;
+ }
+ if (this.isInitialRender && this.parentAnimationsTriggered) {
+ if (!this.areCharactersEqualized()) {
+ this.ensureComponentsRendered()
+ .then(() => {
+ this.equalizeCharactersAndStyleConnection();
+ this.animateContentIn();
+ })
+ .catch(e => console.error(e));
+ } else {
+ this.animateContentIn();
+ }
+ }
+ }
}
export { BoltTwoCharacterLayout, boltTwoCharacterLayoutIs };
diff --git a/packages/micro-journeys/src/utils/equalize-relative-heights.js b/packages/micro-journeys/src/utils/equalize-relative-heights.js
index 9c06cfa378..0b7faacc0d 100644
--- a/packages/micro-journeys/src/utils/equalize-relative-heights.js
+++ b/packages/micro-journeys/src/utils/equalize-relative-heights.js
@@ -6,6 +6,8 @@
* @prop {HTMLElement} paddingEqualizationTarget
*/
+export const equalizeRelativeHeightsClass = 'isEqualized';
+
/**
*
* @param {EqualizeRelativeHeightArgItem} item
@@ -83,6 +85,7 @@ export const equalizeRelativeHeights = (
paddingEqualizationTarget.style.paddingTop = `calc(${items[
indexOfLongest
].relativeOffsetTop - relativeOffsetTop}px + ${previousPadding})`;
+ paddingEqualizationTarget.classList.add(equalizeRelativeHeightsClass);
}
});
if (callback) {