Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonMilord committed Jan 17, 2025
1 parent 856acde commit 21f673f
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
engine-id={engineId}
fields-to-include-in-citations={config.fieldsToIncludeInCitations}
collapsible={config.collapsible}
max-collapsed-height={config.maxCollapsedHeight}
with-toggle={config.withToggle}
>
</c-quantic-generated-answer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export default class ExampleQuanticGeneratedAnswer extends LightningElement {
description: 'Indicates whether the answer should be collapsible.',
defaultValue: false,
},
{
attribute: 'maxCollapsedHeight',
label: 'Max Collapsed Height',
description:
'The maximum height of the answer when it is collapsed, in pixels.',
defaultValue: 250,
},
{
attribute: 'withToggle',
label: 'With Toggle',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {createElement} from 'lwc';
import QuanticGeneratedAnswer from 'c/quanticGeneratedAnswer';
import * as mockHeadlessLoader from 'c/quanticHeadlessLoader';

let mockAnswerHeight = 300;
let mockAnswerHeight = 250;

jest.mock('c/quanticHeadlessLoader');
jest.mock('c/quanticUtils', () => ({
Expand All @@ -31,6 +31,7 @@ const defaultOptions = {
answerConfigurationId: undefined,
withToggle: false,
collapsible: false,
maxCollapsedHeight: 250,
};

function createTestComponent(options = defaultOptions) {
Expand Down Expand Up @@ -101,7 +102,7 @@ const exampleEngine = {
id: 'dummy engine',
};
let isInitialized = false;
const maximumAnswerHeight = 250;
const defaultAnswerHeight = 250;

function prepareHeadlessState() {
// @ts-ignore
Expand Down Expand Up @@ -342,7 +343,7 @@ describe('c-quantic-generated-answer', () => {
describe('when the property collapsible is set to true', () => {
describe('when the answer is shorter than the maximum answer height', () => {
beforeEach(() => {
mockAnswerHeight = maximumAnswerHeight - 100;
mockAnswerHeight = defaultAnswerHeight - 100;
});

it('should not display the generating answer message', async () => {
Expand All @@ -363,7 +364,7 @@ describe('c-quantic-generated-answer', () => {

describe('when the answer is longer than the maximum answer height', () => {
beforeEach(() => {
mockAnswerHeight = maximumAnswerHeight + 100;
mockAnswerHeight = defaultAnswerHeight + 100;
});

it('should display the generating answer message', async () => {
Expand Down Expand Up @@ -489,7 +490,7 @@ describe('c-quantic-generated-answer', () => {
describe('when the property collapsible is set to true', () => {
describe('when the answer is shorter than the maximum answer height', () => {
beforeEach(() => {
mockAnswerHeight = maximumAnswerHeight - 100;
mockAnswerHeight = defaultAnswerHeight - 100;
});

it('should not display the generating answer message', async () => {
Expand Down Expand Up @@ -525,7 +526,7 @@ describe('c-quantic-generated-answer', () => {

describe('when the answer is longer than the maximum answer height', () => {
beforeEach(() => {
mockAnswerHeight = maximumAnswerHeight + 100;
mockAnswerHeight = defaultAnswerHeight + 100;
});

it('should not display the generating answer message', async () => {
Expand Down Expand Up @@ -558,6 +559,82 @@ describe('c-quantic-generated-answer', () => {
expect(generatedAnswerCollapseToggle).not.toBeNull();
});
});

describe('when the property maxCollapsedHeight is set to a custom value', () => {
// The valid range is between 150 and 500 pixels.
describe('when the value is within the valid range', () => {
beforeEach(() => {
mockAnswerHeight = defaultAnswerHeight - 25;
});

it('should set the answer height with the custom value', async () => {
const expectedAnswerHeightValue = 300;
const element = createTestComponent({
...defaultOptions,
maxCollapsedHeight: expectedAnswerHeightValue,
});
await flushPromises();

const generatedAnswer = element.shadowRoot.querySelector(
'.generated-answer__answer'
);
expect(generatedAnswer).not.toBeNull();
const computedStyle = getComputedStyle(generatedAnswer);
console.log(
'computedStyle: ' + JSON.stringify(computedStyle.maxHeight)
);
expect(computedStyle.maxHeight).toEqual(
`${expectedAnswerHeightValue}px`
);
});
});

describe('when the value is greater than the valid range', () => {
beforeEach(() => {
mockAnswerHeight = defaultAnswerHeight + 100;
});

it('should set the answer height with the fallback default value', async () => {
const element = createTestComponent({
...defaultOptions,
maxCollapsedHeight: 550,
});
await flushPromises();

const generatedAnswer = element.shadowRoot.querySelector(
'.generated-answer__answer'
);
expect(generatedAnswer).not.toBeNull();
const computedStyle = getComputedStyle(generatedAnswer);
expect(computedStyle.maxHeight).toEqual(
`${defaultAnswerHeight}px`
);
});
});

describe('when the value is smaller than the valid range', () => {
beforeEach(() => {
mockAnswerHeight = defaultAnswerHeight;
});

it('should set the answer height with the fallback default value', async () => {
const element = createTestComponent({
...defaultOptions,
maxCollapsedHeight: 100,
});
await flushPromises();

const generatedAnswer = element.shadowRoot.querySelector(
'.generated-answer__answer'
);
expect(generatedAnswer).not.toBeNull();
const computedStyle = getComputedStyle(generatedAnswer);
expect(computedStyle.maxHeight).toEqual(
`${defaultAnswerHeight}px`
);
});
});
});
});

it('should display the generated answer content', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ const FEEDBACK_LIKED_STATE = 'liked';
const FEEDBACK_DISLIKED_STATE = 'disliked';
const FEEDBACK_NEUTRAL_STATE = 'neutral';

const DEFAULT_COLLAPSED_HEIGHT = 250;
const MAX_COLLAPSED_HEIGHT = 500;
const MIN_COLLAPSED_HEIGHT = 150;

const GENERATED_ANSWER_DATA_KEY = 'coveo-generated-answer-data';

/**
Expand Down Expand Up @@ -86,6 +90,13 @@ export default class QuanticGeneratedAnswer extends LightningElement {
* @type {string}
*/
@api answerConfigurationId;
/**
* The maximum height (in px units) of the generated answer when it is collapsed.
* @api
* @type {number}
* @default {250}
*/
@api maxCollapsedHeight = DEFAULT_COLLAPSED_HEIGHT;

labels = {
generatedAnswerForYou,
Expand Down Expand Up @@ -125,8 +136,6 @@ export default class QuanticGeneratedAnswer extends LightningElement {
ariaLiveMessage;
/** @type {boolean} */
hasInitializationError = false;
/** @type {number} */
_maximumAnswerHeight = 250;
/** @type {boolean} */
_exceedsMaximumHeight = false;
/** @type {boolean} */
Expand All @@ -153,11 +162,7 @@ export default class QuanticGeneratedAnswer extends LightningElement {
if (this.collapsible) {
// If we are still streaming add a little extra height to the answer element to account for the next answer chunk.
// This helps a lot with the jankyness of the answer fading out when the chunk is close but not yet over the max height.
const answerElementHeight = this.isStreaming
? this.generatedAnswerElementHeight + 50
: this.generatedAnswerElementHeight;
this._exceedsMaximumHeight =
answerElementHeight > this._maximumAnswerHeight;
this._exceedsMaximumHeight = this.isMaximumHeightExceeded();
}
}

Expand Down Expand Up @@ -259,6 +264,15 @@ export default class QuanticGeneratedAnswer extends LightningElement {
}
}

isMaximumHeightExceeded() {
const maximumAnswerHeight = this.validateMaxCollapsedHeight();
const answerElementHeight = this.isStreaming
? this.generatedAnswerElementHeight + 50
: this.generatedAnswerElementHeight;

return answerElementHeight > maximumAnswerHeight;
}

/**
* handles hovering over a citation.
* @param {string} id
Expand Down Expand Up @@ -348,6 +362,14 @@ export default class QuanticGeneratedAnswer extends LightningElement {
}
};

handleAnswerDoneGenerating = (event) => {
event.stopPropagation();
if (this.collapsible) {
this._exceedsMaximumHeight = this.isMaximumHeightExceeded();
}
this.updateGeneratedAnswerCSSVariables();
};

handleToggleCollapseAnswer() {
this.state?.expanded
? this.generatedAnswer.collapse()
Expand Down Expand Up @@ -385,13 +407,30 @@ export default class QuanticGeneratedAnswer extends LightningElement {
}

/**
* Sets the the value of the CSS variable "--maxHeight" the value of the _maximumAnswerHeight property.
* Sets the the value of the CSS variable "--maxHeight" the value of the maxCollapsedHeight property.
*/
updateGeneratedAnswerCSSVariables() {
if (this._exceedsMaximumHeight) {
const styles = this.generatedAnswerElement?.style;
styles.setProperty('--maxHeight', `${this._maximumAnswerHeight}px`);
styles.setProperty('--maxHeight', `${this.maxCollapsedHeight}px`);
}
}

/**
* Validates that the value of the maxCollapsedHeight property is within acceptable bounds.
*/
validateMaxCollapsedHeight() {
const isValid =
this.maxCollapsedHeight >= MIN_COLLAPSED_HEIGHT &&
this.maxCollapsedHeight <= MAX_COLLAPSED_HEIGHT;

if (!isValid) {
console.warn(
`max-collapsed-height (${this.maxCollapsedHeight}px) must be between ${MIN_COLLAPSED_HEIGHT} and ${MAX_COLLAPSED_HEIGHT}. Falling back to default value: ${DEFAULT_COLLAPSED_HEIGHT}px.`
);
}

return isValid ? this.maxCollapsedHeight : DEFAULT_COLLAPSED_HEIGHT;
}

get answer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
answer-content-format={answerContentFormat}
answer={answer}
is-streaming={isStreaming}
onquantic__answergenerated={handleAnswerDoneGenerating}
data-cy="generated-answer__content"
>
</c-quantic-generated-answer-content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import generatedTextContentTemplate from './templates/generatedTextContent.html'
/**
* The `QuanticGeneratedAnswerContent` component displays the generated answer content.
* @category Internal
* @fires CustomEvent#quantic__answergenerated
* @example
* <c-quantic-generated-answer-content answer-content-format={answerContentFormat} answer={answer} is-streaming={isStreaming}></c-quantic-generated-answer-content>
*/
Expand Down Expand Up @@ -110,6 +111,7 @@ export default class QuanticGeneratedAnswerContent extends LightningElement {
else {
answerContainer.textContent = this.answer;
}
this.dispatchEvent(new CustomEvent('quantic__answergenerated'));
}

get generatedAnswerContentClass() {
Expand Down

0 comments on commit 21f673f

Please sign in to comment.