From 5e4ba2df0b421e3633f0783c8106aa62dfeb9db9 Mon Sep 17 00:00:00 2001 From: Alexandre Bourdin Date: Wed, 2 Jul 2025 11:33:01 +0200 Subject: [PATCH] Merge pull request #26683 from overleaf/ab-update-survey-form Update survey form and preview + support custom button CTA GitOrigin-RevId: 2b519ab1da1c8e7897b3135956f80619f4e901b4 --- .../app/src/Features/Survey/SurveyHandler.mjs | 6 +- .../app/src/Features/Survey/SurveyManager.js | 7 ++- services/web/app/src/models/Survey.js | 8 ++- .../components/sidebar/sidebar.tsx | 49 ---------------- .../components/survey-widget-ds-nav.tsx | 6 +- .../project-list/components/survey-widget.tsx | 43 -------------- ...s.tsx => survey-widget-ds-nav.stories.tsx} | 15 ++--- .../components/survey-widget.test.tsx | 56 +++++++++++++++---- .../web/types/project/dashboard/survey.d.ts | 5 +- 9 files changed, 71 insertions(+), 124 deletions(-) delete mode 100644 services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx delete mode 100644 services/web/frontend/js/features/project-list/components/survey-widget.tsx rename services/web/frontend/stories/project-list/{survey-widget.stories.tsx => survey-widget-ds-nav.stories.tsx} (55%) diff --git a/services/web/app/src/Features/Survey/SurveyHandler.mjs b/services/web/app/src/Features/Survey/SurveyHandler.mjs index 7ce89594c6..db857d9fd3 100644 --- a/services/web/app/src/Features/Survey/SurveyHandler.mjs +++ b/services/web/app/src/Features/Survey/SurveyHandler.mjs @@ -14,7 +14,7 @@ import UserGetter from '../User/UserGetter.js' * determines if there is a survey to show, given current surveys and rollout percentages * uses userId in computation, to ensure that rollout groups always contain same users * @param {string} userId - * @returns {Promise} + * @returns {Promise | undefined>} */ async function getSurvey(userId) { const survey = await SurveyCache.get(true) @@ -27,7 +27,7 @@ async function getSurvey(userId) { } } - const { name, preText, linkText, url, options } = survey?.toObject() || {} + const { name, title, text, cta, url, options } = survey?.toObject() || {} // default to full rollout for backwards compatibility const rolloutPercentage = options?.rolloutPercentage || 100 if (!_userInRolloutPercentile(userId, name, rolloutPercentage)) { @@ -53,7 +53,7 @@ async function getSurvey(userId) { } } - return { name, preText, linkText, url } + return { name, title, text, cta, url } } } diff --git a/services/web/app/src/Features/Survey/SurveyManager.js b/services/web/app/src/Features/Survey/SurveyManager.js index 3d6f225e10..7eac05f5d3 100644 --- a/services/web/app/src/Features/Survey/SurveyManager.js +++ b/services/web/app/src/Features/Survey/SurveyManager.js @@ -9,15 +9,16 @@ async function getSurvey() { } } -async function updateSurvey({ name, preText, linkText, url, options }) { +async function updateSurvey({ name, title, text, cta, url, options }) { validateOptions(options) let survey = await getSurvey() if (!survey) { survey = new Survey() } survey.name = name - survey.preText = preText - survey.linkText = linkText + survey.title = title + survey.text = text + survey.cta = cta survey.url = url survey.options = options await survey.save() diff --git a/services/web/app/src/models/Survey.js b/services/web/app/src/models/Survey.js index deb5d60454..bd1582a08b 100644 --- a/services/web/app/src/models/Survey.js +++ b/services/web/app/src/models/Survey.js @@ -19,14 +19,18 @@ const SurveySchema = new Schema( message: `invalid, must match: ${NAME_REGEX}`, }, }, - preText: { + title: { type: String, required: true, }, - linkText: { + text: { type: String, required: true, }, + cta: { + type: String, + required: false, + }, url: { type: String, required: true, diff --git a/services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx b/services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx deleted file mode 100644 index 55bab83261..0000000000 --- a/services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import NewProjectButton from '../new-project-button' -import SidebarFilters from './sidebar-filters' -import AddAffiliation, { useAddAffiliation } from '../add-affiliation' -import SurveyWidget from '../survey-widget' -import { usePersistedResize } from '../../../../shared/hooks/use-resize' - -function Sidebar() { - const { show: showAddAffiliationWidget } = useAddAffiliation() - const { mousePos, getHandleProps, getTargetProps } = usePersistedResize({ - name: 'project-sidebar', - }) - - return ( -
-
- -
- -
-
-
-
- ) -} - -export default Sidebar diff --git a/services/web/frontend/js/features/project-list/components/survey-widget-ds-nav.tsx b/services/web/frontend/js/features/project-list/components/survey-widget-ds-nav.tsx index 5fcc0f7e3d..9264b3288d 100644 --- a/services/web/frontend/js/features/project-list/components/survey-widget-ds-nav.tsx +++ b/services/web/frontend/js/features/project-list/components/survey-widget-ds-nav.tsx @@ -26,8 +26,8 @@ export function SurveyWidgetDsNav() {
-

{survey.preText}

-

{survey.linkText}

+

{survey.title}

+

{survey.text}

- {t('take_survey')} + {survey.cta || t('take_survey')}
{ - setDismissedSurvey(true) - }, [setDismissedSurvey]) - - if (!survey?.name || dismissedSurvey) { - return null - } - - return ( -
-
-
-
- {survey.preText}  - - {survey.linkText} - -
-
- dismissSurvey()} /> -
-
-
-
- ) -} diff --git a/services/web/frontend/stories/project-list/survey-widget.stories.tsx b/services/web/frontend/stories/project-list/survey-widget-ds-nav.stories.tsx similarity index 55% rename from services/web/frontend/stories/project-list/survey-widget.stories.tsx rename to services/web/frontend/stories/project-list/survey-widget-ds-nav.stories.tsx index c1437a789e..8e9e0d1a40 100644 --- a/services/web/frontend/stories/project-list/survey-widget.stories.tsx +++ b/services/web/frontend/stories/project-list/survey-widget-ds-nav.stories.tsx @@ -1,31 +1,32 @@ -import SurveyWidget from '../../js/features/project-list/components/survey-widget' +import { SurveyWidgetDsNav } from '@/features/project-list/components/survey-widget-ds-nav' export const Survey = (args: any) => { localStorage.clear() window.metaAttributesCache.set('ol-survey', { name: 'my-survey', - preText: 'To help shape the future of Overleaf', - linkText: 'Click here!', + title: 'To help shape the future of Overleaf', + text: 'Click here!', + cta: 'Let’s go!', url: 'https://example.com/my-survey', }) - return + return } export const UndefinedSurvey = (args: any) => { localStorage.clear() - return + return } export const EmptySurvey = (args: any) => { localStorage.clear() window.metaAttributesCache.set('ol-survey', {}) - return + return } export default { title: 'Project List / Survey Widget', - component: SurveyWidget, + component: SurveyWidgetDsNav, } diff --git a/services/web/test/frontend/features/project-list/components/survey-widget.test.tsx b/services/web/test/frontend/features/project-list/components/survey-widget.test.tsx index 59f6bb9680..0c0d4f01a2 100644 --- a/services/web/test/frontend/features/project-list/components/survey-widget.test.tsx +++ b/services/web/test/frontend/features/project-list/components/survey-widget.test.tsx @@ -1,13 +1,14 @@ import { expect } from 'chai' import { fireEvent, render, screen } from '@testing-library/react' -import { SurveyWidgetDsNav } from '../../../../../frontend/js/features/project-list/components/survey-widget-ds-nav' +import { SurveyWidgetDsNav } from '@/features/project-list/components/survey-widget-ds-nav' import { SplitTestProvider } from '@/shared/context/split-test-context' describe('', function () { beforeEach(function () { this.name = 'my-survey' - this.preText = 'To help shape the future of Overleaf' - this.linkText = 'Click here!' + this.title = 'To help shape the future of Overleaf' + this.text = 'Click here!' + this.cta = 'Let’s go!' this.url = 'https://example.com/my-survey' localStorage.clear() @@ -17,8 +18,8 @@ describe('', function () { beforeEach(function () { window.metaAttributesCache.set('ol-survey', { name: this.name, - preText: this.preText, - linkText: this.linkText, + title: this.title, + text: this.text, url: this.url, }) @@ -33,8 +34,8 @@ describe('', function () { const dismissed = localStorage.getItem('dismissed-my-survey') expect(dismissed).to.equal(null) - screen.getByText(this.preText) - screen.getByText(this.linkText) + screen.getByText(this.title) + screen.getByText(this.text) const link = screen.getByRole('link', { name: 'Take survey', @@ -48,7 +49,7 @@ describe('', function () { }) fireEvent.click(dismissButton) - const text = screen.queryByText(this.preText) + const text = screen.queryByText(this.title) expect(text).to.be.null const link = screen.queryByRole('button') @@ -59,12 +60,43 @@ describe('', function () { }) }) + describe('survey widget is visible with custom CTA', function () { + beforeEach(function () { + window.metaAttributesCache.set('ol-survey', { + name: this.name, + title: this.title, + text: this.text, + cta: this.cta, + url: this.url, + }) + + render( + + + + ) + }) + + it('shows text and link with custom CTA', function () { + const dismissed = localStorage.getItem('dismissed-my-survey') + expect(dismissed).to.equal(null) + + screen.getByText(this.title) + screen.getByText(this.text) + + const link = screen.getByRole('link', { + name: this.cta, + }) as HTMLAnchorElement + expect(link.href).to.equal(this.url) + }) + }) + describe('survey widget is not shown when already dismissed', function () { beforeEach(function () { window.metaAttributesCache.set('ol-survey', { name: this.name, - preText: this.preText, - linkText: this.linkText, + title: this.title, + text: this.text, url: this.url, }) localStorage.setItem('dismissed-my-survey', 'true') @@ -77,7 +109,7 @@ describe('', function () { }) it('nothing is displayed', function () { - const text = screen.queryByText(this.preText) + const text = screen.queryByText(this.title) expect(text).to.be.null const link = screen.queryByRole('button') @@ -95,7 +127,7 @@ describe('', function () { }) it('nothing is displayed', function () { - const text = screen.queryByText(this.preText) + const text = screen.queryByText(this.title) expect(text).to.be.null const link = screen.queryByRole('button') diff --git a/services/web/types/project/dashboard/survey.d.ts b/services/web/types/project/dashboard/survey.d.ts index f43b454196..2dc3d9b7ec 100644 --- a/services/web/types/project/dashboard/survey.d.ts +++ b/services/web/types/project/dashboard/survey.d.ts @@ -1,6 +1,7 @@ export type Survey = { name: string - preText: string - linkText: string + title: string + text: string + cta?: string url: string }