Просмотр исходного кода

Implement 'Add Affiliation' UI on the mobile version of the new react dashboard (#11606)

* Refactor & change `add-affiliation.tsx` file:
    * Change class from `user-profile` to `add-affiliation`
    * Add new optional `className` props for custom styling

* Show affiliation widget on mobile screen:

* Fix bug on use add affiliation hooks: return type should be `boolean`, not `boolean | 0`

GitOrigin-RevId: 800b951e2a012797266b1780993b4ed9a918d565
M Fahru 3 лет назад
Родитель
Сommit
5f3ef71e43

+ 1 - 1
services/web/app/views/project/list/side-bar.pug

@@ -127,7 +127,7 @@ if (isOverleaf)
 		.row-spaced#userProfileInformation(ng-if="hasProjects")
 			div(ng-hide="withAffiliations", ng-cloak)
 				hr
-				.text-centered.user-profile
+				.text-centered.add-affiliation
 					p Are you affiliated with an institution?
 					
 					a.btn.btn-secondary-info.btn-secondary(

+ 16 - 7
services/web/frontend/js/features/project-list/components/sidebar/add-affiliation.tsx → services/web/frontend/js/features/project-list/components/add-affiliation.tsx

@@ -1,19 +1,26 @@
 import { useTranslation } from 'react-i18next'
 import { Button } from 'react-bootstrap'
-import { useProjectListContext } from '../../context/project-list-context'
-import getMeta from '../../../../utils/meta'
-import { Affiliation } from '../../../../../../types/affiliation'
-import { ExposedSettings } from '../../../../../../types/exposed-settings'
+import { useProjectListContext } from '../context/project-list-context'
+import getMeta from '../../../utils/meta'
+import { Affiliation } from '../../../../../types/affiliation'
+import { ExposedSettings } from '../../../../../types/exposed-settings'
+import classNames from 'classnames'
 
 export function useAddAffiliation() {
   const { totalProjectsCount } = useProjectListContext()
   const { isOverleaf } = getMeta('ol-ExposedSettings') as ExposedSettings
   const userAffiliations = getMeta('ol-userAffiliations', []) as Affiliation[]
 
-  return { show: isOverleaf && totalProjectsCount && !userAffiliations.length }
+  return {
+    show: isOverleaf && totalProjectsCount > 0 && !userAffiliations.length,
+  }
+}
+
+type AddAffiliationProps = {
+  className?: string
 }
 
-function AddAffiliation() {
+function AddAffiliation({ className }: AddAffiliationProps) {
   const { t } = useTranslation()
   const { show } = useAddAffiliation()
 
@@ -21,8 +28,10 @@ function AddAffiliation() {
     return null
   }
 
+  const classes = classNames('text-centered', 'add-affiliation', className)
+
   return (
-    <div className="text-centered user-profile">
+    <div className={classes}>
       <p>{t('are_you_affiliated_with_an_institution')}</p>
       <Button
         bsStyle={null}

+ 12 - 0
services/web/frontend/js/features/project-list/components/new-project-button.tsx

@@ -8,6 +8,7 @@ import getMeta from '../../../utils/meta'
 import NewProjectButtonModal, {
   NewProjectButtonModalVariant,
 } from './new-project-button/new-project-button-modal'
+import AddAffiliation, { useAddAffiliation } from './add-affiliation'
 import { Nullable } from '../../../../../types/utils'
 import { sendMB } from '../../../infrastructure/event-tracking'
 
@@ -32,6 +33,7 @@ type NewProjectButtonProps = {
   className?: string
   menuClassName?: string
   trackingKey?: string
+  showAddAffiliationWidget?: boolean
 }
 
 function NewProjectButton({
@@ -40,12 +42,14 @@ function NewProjectButton({
   className,
   menuClassName,
   trackingKey,
+  showAddAffiliationWidget,
 }: NewProjectButtonProps) {
   const { t } = useTranslation()
   const { templateLinks } = getMeta('ol-ExposedSettings') as ExposedSettings
   const [modal, setModal] =
     useState<Nullable<NewProjectButtonModalVariant>>(null)
   const portalTemplates = getMeta('ol-portalTemplates') as PortalTemplate[]
+  const { show: enableAddAffiliationWidget } = useAddAffiliation()
 
   const sendTrackingEvent = useCallback(
     ({
@@ -223,6 +227,14 @@ function NewProjectButton({
                 : templateLink.name}
             </MenuItem>
           ))}
+          {showAddAffiliationWidget && enableAddAffiliationWidget ? (
+            <>
+              <MenuItem divider />
+              <li className="add-affiliation-mobile-wrapper">
+                <AddAffiliation className="is-mobile" />
+              </li>
+            </>
+          ) : null}
         </Dropdown.Menu>
       </ControlledDropdown>
       <NewProjectButtonModal modal={modal} onHide={() => setModal(null)} />

+ 1 - 0
services/web/frontend/js/features/project-list/components/project-list-root.tsx

@@ -104,6 +104,7 @@ function ProjectListPageContent() {
                         <NewProjectButton
                           id="new-project-button-projects-table"
                           className="pull-left me-2"
+                          showAddAffiliationWidget
                         />
                         <SearchForm
                           inputValue={searchText}

+ 1 - 1
services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx

@@ -1,6 +1,6 @@
 import NewProjectButton from '../new-project-button'
 import SidebarFilters from './sidebar-filters'
-import AddAffiliation, { useAddAffiliation } from './add-affiliation'
+import AddAffiliation, { useAddAffiliation } from '../add-affiliation'
 import { usePersistedResize } from '../../../../shared/hooks/use-resize'
 
 function Sidebar() {

+ 1 - 1
services/web/frontend/stories/project-list/add-affiliation.stories.tsx

@@ -1,4 +1,4 @@
-import AddAffiliation from '../../js/features/project-list/components/sidebar/add-affiliation'
+import AddAffiliation from '../../js/features/project-list/components/add-affiliation'
 import { ProjectListProvider } from '../../js/features/project-list/context/project-list-context'
 import useFetchMock from '../hooks/use-fetch-mock'
 import { projectsData } from '../../../test/frontend/features/project-list/fixtures/projects-data'

+ 9 - 1
services/web/frontend/stylesheets/app/project-list.less

@@ -217,7 +217,11 @@ input.project-list-table-select-item[type='checkbox'] {
   text-align: center;
 }
 
-.user-profile {
+.add-affiliation-mobile-wrapper {
+  padding: @padding-md 0;
+}
+
+.add-affiliation {
   .progress {
     height: @line-height-computed / 2;
     margin-bottom: @line-height-computed / 4;
@@ -225,6 +229,10 @@ input.project-list-table-select-item[type='checkbox'] {
   p {
     margin-bottom: @line-height-computed / 4;
   }
+  &.is-mobile p {
+    font-size: 12px;
+    white-space: normal;
+  }
 }
 
 .user-notifications {

+ 9 - 3
services/web/test/frontend/features/project-list/components/new-project-button.test.tsx

@@ -1,8 +1,14 @@
-import { render, fireEvent, screen } from '@testing-library/react'
+import { fireEvent, screen } from '@testing-library/react'
 import { expect } from 'chai'
+import fetchMock from 'fetch-mock'
 import NewProjectButton from '../../../../../frontend/js/features/project-list/components/new-project-button'
+import { renderWithProjectListContext } from '../helpers/render-with-context'
 
 describe('<NewProjectButton />', function () {
+  beforeEach(function () {
+    fetchMock.reset()
+  })
+
   describe('for every user (affiliated and non-affiliated)', function () {
     beforeEach(function () {
       window.metaAttributesCache.set('ol-ExposedSettings', {
@@ -18,7 +24,7 @@ describe('<NewProjectButton />', function () {
         ],
       })
 
-      render(<NewProjectButton id="test" />)
+      renderWithProjectListContext(<NewProjectButton id="test" />)
 
       const newProjectButton = screen.getByRole('button', {
         name: 'New Project',
@@ -100,7 +106,7 @@ describe('<NewProjectButton />', function () {
     })
 
     it('shows the correct dropdown menu', function () {
-      render(<NewProjectButton id="test" />)
+      renderWithProjectListContext(<NewProjectButton id="test" />)
 
       const newProjectButton = screen.getByRole('button', {
         name: 'New Project',

+ 4 - 2
services/web/test/frontend/features/project-list/components/sidebar/add-affiliation.test.tsx

@@ -2,7 +2,7 @@ import { screen, waitFor } from '@testing-library/react'
 import { expect } from 'chai'
 import fetchMock from 'fetch-mock'
 import { renderWithProjectListContext } from '../../helpers/render-with-context'
-import AddAffiliation from '../../../../../../frontend/js/features/project-list/components/sidebar/add-affiliation'
+import AddAffiliation from '../../../../../../frontend/js/features/project-list/components/add-affiliation'
 import { Affiliation } from '../../../../../../types/affiliation'
 
 describe('Add affiliation widget', function () {
@@ -54,7 +54,9 @@ describe('Add affiliation widget', function () {
     window.metaAttributesCache.set('ol-ExposedSettings', { isOverleaf: true })
     window.metaAttributesCache.set('ol-userAffiliations', [])
 
-    renderWithProjectListContext(<AddAffiliation />, { projects: [] })
+    renderWithProjectListContext(<AddAffiliation />, {
+      projects: [],
+    })
 
     await fetchMock.flush(true)
     await waitFor(() => expect(fetchMock.called('/api/project')))