[web] Show DS Navigation for all SAAS users regardless of feature flag (with fix!) (#23821)

* Reapply "[web] Show DS Navigation for all SAAS users regardless of feature fla…"

This reverts commit 8d4096a7e4b6a7894b1bef14290548225deebd57.

* Fix: `hasDsNav` should check `isOverleaf` !!!

* Change `hasDsNav` from a boolean to a function call for dynamic evaluation

GitOrigin-RevId: 17463ac113a4278ed344622552d83da93b208b4d
This commit is contained in:
Antoine Clausse
2025-02-24 11:07:22 +01:00
committed by Copybot
parent 78d48dbdac
commit 462c2d7bd3
9 changed files with 49 additions and 31 deletions

View File

@@ -1,5 +1,4 @@
import { useCallback } from 'react'
import { useSplitTestContext } from '@/shared/context/split-test-context'
import { sendMB } from '@/infrastructure/event-tracking'
export type ExtraSegmentations = {
@@ -39,16 +38,11 @@ export type ExtraSegmentations = {
}
export const useSendProjectListMB = () => {
const { splitTestVariants } = useSplitTestContext()
const hasDsNav = splitTestVariants['sidebar-navigation-ui-update'] as
| 'default'
| 'active'
return useCallback(
<T extends keyof ExtraSegmentations>(
event: T,
payload: ExtraSegmentations[T]
) =>
sendMB(event, { ...payload, 'sidebar-navigation-ui-update': hasDsNav }),
[hasDsNav]
) => sendMB(event, payload),
[]
)
}

View File

@@ -20,7 +20,7 @@ import ProjectListDefault from '@/features/project-list/components/project-list-
import { ProjectListDsNav } from '@/features/project-list/components/project-list-ds-nav'
import {
DsNavStyleProvider,
useIsDsNav,
hasDsNav,
} from '@/features/project-list/components/use-is-ds-nav'
function ProjectListRoot() {
@@ -82,14 +82,12 @@ function ProjectListPageContent() {
const { t } = useTranslation()
const hasDsNav = useIsDsNav()
if (isLoading) {
const loadingComponent = (
<LoadingBranded loadProgress={loadProgress} label={t('loading')} />
)
if (hasDsNav) {
if (hasDsNav()) {
return loadingComponent
} else {
return (
@@ -106,7 +104,7 @@ function ProjectListPageContent() {
<WelcomePageContent />
</DefaultPageContentWrapper>
)
} else if (hasDsNav) {
} else if (hasDsNav()) {
return (
<DsNavStyleProvider>
<ProjectListDsNav />

View File

@@ -5,7 +5,7 @@ import {
} from '../../context/project-list-context'
import TagsList from './tags-list'
import ProjectsFilterMenu from '../projects-filter-menu'
import { useIsDsNav } from '@/features/project-list/components/use-is-ds-nav'
import { hasDsNav } from '@/features/project-list/components/use-is-ds-nav'
type SidebarFilterProps = {
filter: Filter
@@ -30,7 +30,6 @@ export function SidebarFilter({ filter, text }: SidebarFilterProps) {
export default function SidebarFilters() {
const { t } = useTranslation()
const hasDsNav = useIsDsNav()
return (
<ul className="list-unstyled project-list-filters">
@@ -39,7 +38,7 @@ export default function SidebarFilters() {
<SidebarFilter filter="shared" text={t('shared_with_you')} />
<SidebarFilter filter="archived" text={t('archived_projects')} />
<SidebarFilter filter="trashed" text={t('trashed_projects')} />
{hasDsNav && (
{hasDsNav() && (
<li role="none">
<hr />
</li>

View File

@@ -14,7 +14,7 @@ import {
DropdownMenu,
DropdownToggle,
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
import { useIsDsNav } from '@/features/project-list/components/use-is-ds-nav'
import { hasDsNav } from '@/features/project-list/components/use-is-ds-nav'
export default function TagsList() {
const { t } = useTranslation()
@@ -35,7 +35,6 @@ export default function TagsList() {
DeleteTagModal,
} = useTag()
const isDsNav = useIsDsNav()
return (
<>
<li
@@ -43,11 +42,11 @@ export default function TagsList() {
aria-hidden="true"
data-testid="organize-projects"
>
{isDsNav ? t('organize_tags') : t('organize_projects')}
{hasDsNav() ? t('organize_tags') : t('organize_projects')}
</li>
<li className="tag">
<button type="button" className="tag-name" onClick={openCreateTagModal}>
{isDsNav ? (
{hasDsNav() ? (
<Plus weight="bold" />
) : (
<MaterialIcon type="add" className="tag-list-icon" />
@@ -74,7 +73,7 @@ export default function TagsList() {
color: getTagColor(tag),
}}
>
{isDsNav ? (
{hasDsNav() ? (
<TagSimple weight="fill" className="tag-list-icon" />
) : (
<MaterialIcon type="label" className="tag-list-icon" />
@@ -94,7 +93,7 @@ export default function TagsList() {
id={`${tag._id}-dropdown-toggle`}
data-testid="tag-dropdown-toggle"
>
{isDsNav && <DotsThreeVertical weight="bold" />}
{hasDsNav() && <DotsThreeVertical weight="bold" />}
</DropdownToggle>
<DropdownMenu className="dropdown-menu-sm-width">
<DropdownItem

View File

@@ -5,6 +5,7 @@ import classnames from 'classnames'
import OLButton from '@/features/ui/components/ol/ol-button'
import { useTranslation } from 'react-i18next'
import { X } from '@phosphor-icons/react'
import { useHideDsSurvey } from '@/features/project-list/components/use-is-ds-nav'
export function SurveyWidgetDsNav() {
const { t } = useTranslation()
@@ -13,6 +14,7 @@ export function SurveyWidgetDsNav() {
`dismissed-${survey?.name}`,
false
)
const hideDsSurvey = useHideDsSurvey()
const dismissSurvey = useCallback(() => {
setDismissedSurvey(true)
@@ -22,6 +24,12 @@ export function SurveyWidgetDsNav() {
return null
}
// Hide the survey for users who have sidebar-navigation-ui-update:
// They've had it for months. We don't need their feedback anymore
if (hideDsSurvey && survey?.name === 'ds-nav') {
return null
}
return (
<div className={classnames('user-notifications', `survey-${survey.name}`)}>
<div className="notification-entry">

View File

@@ -1,10 +1,13 @@
import { createContext, type FC, type ReactNode, useContext } from 'react'
import { useSplitTestContext } from '@/shared/context/split-test-context'
import getMeta from '@/utils/meta'
export const hasDsNav = () => getMeta('ol-ExposedSettings').isOverleaf
/**
* This hook returns whether the user has the split-test assignment 'sidebar-navigation-ui-update'
*/
export const useIsDsNav = () => {
export const useHideDsSurvey = () => {
const { splitTestVariants } = useSplitTestContext()
return splitTestVariants['sidebar-navigation-ui-update'] === 'active'
}

View File

@@ -20,6 +20,7 @@ describe('<TagsList />', function () {
project_ids: [projectsData[0].id, projectsData[1].id],
},
])
window.metaAttributesCache.set('ol-ExposedSettings', { isOverleaf: true })
fetchMock.post('/tag', {
_id: 'eee888eee888',
@@ -42,7 +43,7 @@ describe('<TagsList />', function () {
it('displays the tags list', function () {
const header = screen.getByTestId('organize-projects')
expect(header.textContent).to.equal('Organize Projects')
expect(header.textContent).to.equal('Organize Tags')
screen.getByRole('button', {
name: 'New Tag',

View File

@@ -1,8 +1,9 @@
import { expect } from 'chai'
import { fireEvent, render, screen } from '@testing-library/react'
import SurveyWidget from '../../../../../frontend/js/features/project-list/components/survey-widget'
import { SurveyWidgetDsNav } from '../../../../../frontend/js/features/project-list/components/survey-widget-ds-nav'
import { SplitTestProvider } from '@/shared/context/split-test-context'
describe('<SurveyWidget />', function () {
describe('<SurveyWidgetDsNav />', function () {
beforeEach(function () {
this.name = 'my-survey'
this.preText = 'To help shape the future of Overleaf'
@@ -21,7 +22,11 @@ describe('<SurveyWidget />', function () {
url: this.url,
})
render(<SurveyWidget />)
render(
<SplitTestProvider>
<SurveyWidgetDsNav />
</SplitTestProvider>
)
})
it('shows text and link', function () {
@@ -29,9 +34,10 @@ describe('<SurveyWidget />', function () {
expect(dismissed).to.equal(null)
screen.getByText(this.preText)
screen.getByText(this.linkText)
const link = screen.getByRole('link', {
name: this.linkText,
name: 'Take survey',
}) as HTMLAnchorElement
expect(link.href).to.equal(this.url)
})
@@ -63,7 +69,11 @@ describe('<SurveyWidget />', function () {
})
localStorage.setItem('dismissed-my-survey', 'true')
render(<SurveyWidget />)
render(
<SplitTestProvider>
<SurveyWidgetDsNav />
</SplitTestProvider>
)
})
it('nothing is displayed', function () {
@@ -77,7 +87,11 @@ describe('<SurveyWidget />', function () {
describe('survey widget is not shown when no survey is configured', function () {
beforeEach(function () {
render(<SurveyWidget />)
render(
<SplitTestProvider>
<SurveyWidgetDsNav />
</SplitTestProvider>
)
})
it('nothing is displayed', function () {

View File

@@ -63,12 +63,14 @@ describe('<ProjectTools />', function () {
projects,
totalSize: 100,
})
window.metaAttributesCache.set('ol-footer', {
showThinFooter: false,
translatedLanguages: { en: 'English' },
subdomainLang: { en: { lngCode: 'en', url: 'overleaf.com' } },
})
window.metaAttributesCache.set('ol-navbar', {
items: [],
})
fetchMock.get('/system/messages', [])
})