[web] Update project list page main content to DS-nav-bar design (#22307)

* Move some SCSS to project-list-default.scss

* Update project-list-ds-nav.tsx to match design and simplify the SCSS

* Fix survey-notification style: light on the sidebar, dark on the main content

* Replace `withHr` by `useSplitTestContext` hook

* Override the `navbar-btn-padding-h` override.

* Fixup main content padding

Co-authored-by: Tim Down <tim.down@overleaf.com>

* Add padding-top to the logo

* Fixup merges

Add changes from https://github.com/overleaf/internal/pull/22272

---------

Co-authored-by: Tim Down <tim.down@overleaf.com>
GitOrigin-RevId: 8dbc449afbf70388bcf3185413b35234acaad349
This commit is contained in:
Antoine Clausse
2024-12-11 14:57:15 +01:00
committed by Copybot
parent 629579b840
commit 27792199f6
8 changed files with 339 additions and 344 deletions
@@ -1,4 +1,5 @@
import { type JSXElementConstructor, useCallback, useState } from 'react'
import classnames from 'classnames'
import { useTranslation } from 'react-i18next'
import getMeta from '../../../utils/meta'
import NewProjectButtonModal, {
@@ -145,7 +146,10 @@ function NewProjectButton({
return (
<>
<Dropdown className={className} onSelect={handleMainButtonClick}>
<Dropdown
className={classnames('new-project-dropdown', className)}
onSelect={handleMainButtonClick}
>
<DropdownToggle
id={id}
className="new-project-button"
@@ -1,19 +1,31 @@
import { useProjectListContext } from '../context/project-list-context'
import { useTranslation } from 'react-i18next'
import CurrentPlanWidget from './current-plan-widget/current-plan-widget'
import NewProjectButton from './new-project-button'
import ProjectListTable from './table/project-list-table'
import SurveyWidget from './survey-widget'
import UserNotifications from './notifications/user-notifications'
import SearchForm from './search-form'
import ProjectsDropdown from './dropdown/projects-dropdown'
import SortByDropdown from './dropdown/sort-by-dropdown'
import ProjectTools from './table/project-tools/project-tools'
import ProjectListTitle from './title/project-list-title'
import LoadMore from './load-more'
import OLCol from '@/features/ui/components/ol/ol-col'
import OLRow from '@/features/ui/components/ol/ol-row'
import { TableContainer } from '@/features/ui/components/bootstrap-5/table'
import DashApiError from '@/features/project-list/components/dash-api-error'
import getMeta from '@/utils/meta'
import DefaultNavbar from '@/features/ui/components/bootstrap-5/navbar/default-navbar'
import FatFooter from '@/features/ui/components/bootstrap-5/footer/fat-footer'
import NewProjectButton from '@/features/project-list/components/new-project-button'
import CurrentPlanWidget from '@/features/project-list/components/current-plan-widget/current-plan-widget'
import ProjectTools from '@/features/project-list/components/table/project-tools/project-tools'
import { useProjectListContext } from '@/features/project-list/context/project-list-context'
import SearchForm from '@/features/project-list/components/search-form'
import { TableContainer } from '@/features/ui/components/bootstrap-5/table'
import ProjectListTable from '@/features/project-list/components/table/project-list-table'
import SidebarDsNav from '@/features/project-list/components/sidebar/sidebar-ds-nav'
export function ProjectListDsNav() {
const navbarProps = getMeta('ol-navbar')
const footerProps = getMeta('ol-footer')
const { t } = useTranslation()
const {
error,
searchText,
setSearchText,
selectedProjects,
@@ -48,29 +60,64 @@ export function ProjectListDsNav() {
customLogo="/img/ol-brand/overleaf-a-ds-solution-mallard.svg"
showAccountButtons={false}
/>
<main className="project-ds-nav-sidebar-and-content">
<main className="project-list-wrapper">
<SidebarDsNav />
<div className="project-ds-nav-content">
<div>Notifications and search and stuff</div>
<div className="project-ds-nav-project-list">
{selectedProjects.length === 0 ? (
<CurrentPlanWidget />
) : (
<ProjectTools />
)}
<SearchForm
inputValue={searchText}
setInputValue={setSearchText}
{error ? <DashApiError /> : ''}
<UserNotifications />
<div className="project-list-header-row">
<ProjectListTitle
filter={filter}
selectedTag={selectedTag}
className="overflow-hidden flex-grow-1"
selectedTagId={selectedTagId}
className="text-truncate d-none d-md-block"
/>
<TableContainer bordered>
{tableTopArea}
<ProjectListTable />
</TableContainer>
<div className="project-tools">
<div className="d-none d-md-block">
{selectedProjects.length === 0 ? (
<CurrentPlanWidget />
) : (
<ProjectTools />
)}
</div>
<div className="d-md-none">
<CurrentPlanWidget />
</div>
</div>
</div>
<div className="project-ds-nav-project-list">
<OLRow className="d-none d-md-block">
<OLCol lg={7}>
<SearchForm
inputValue={searchText}
setInputValue={setSearchText}
filter={filter}
selectedTag={selectedTag}
/>
</OLCol>
</OLRow>
<div className="project-list-sidebar-survey-wrapper d-md-none">
<SurveyWidget />
</div>
<div className="mt-1 d-md-none">
<div
role="toolbar"
className="projects-toolbar"
aria-label={t('projects')}
>
<ProjectsDropdown />
<SortByDropdown />
</div>
</div>
<div className="mt-3">
<TableContainer bordered>
{tableTopArea}
<ProjectListTable />
</TableContainer>
</div>
<div className="mt-3">
<LoadMore />
</div>
</div>
<FatFooter {...footerProps} />
</div>
@@ -12,7 +12,7 @@ function SidebarDsNav() {
return (
<div
className="project-ds-nav-sidebar d-none d-md-flex"
className="project-list-sidebar-wrapper-react d-none d-md-flex"
{...getTargetProps({
style: {
...(mousePos?.x && { flexBasis: `${mousePos.x}px` }),
@@ -21,7 +21,7 @@ function SidebarDsNav() {
>
<NewProjectButton id="new-project-button-sidebar" />
<div className="project-list-sidebar-scroll">
<SidebarFilters withHr />
<SidebarFilters />
{showAddAffiliationWidget && <hr />}
<AddAffiliation />
</div>
@@ -5,6 +5,7 @@ import {
} from '../../context/project-list-context'
import TagsList from './tags-list'
import ProjectsFilterMenu from '../projects-filter-menu'
import { useSplitTestContext } from '@/shared/context/split-test-context'
type SidebarFilterProps = {
filter: Filter
@@ -27,8 +28,11 @@ export function SidebarFilter({ filter, text }: SidebarFilterProps) {
)
}
export default function SidebarFilters({ withHr }: { withHr?: boolean }) {
export default function SidebarFilters() {
const { t } = useTranslation()
const { splitTestVariants } = useSplitTestContext()
const hasDsNav =
splitTestVariants['sidebar-navigation-ui-update'] === 'active'
return (
<ul className="list-unstyled project-list-filters">
@@ -37,7 +41,7 @@ export default function SidebarFilters({ withHr }: { withHr?: boolean }) {
<SidebarFilter filter="shared" text={t('shared_with_you')} />
<SidebarFilter filter="archived" text={t('archived_projects')} />
<SidebarFilter filter="trashed" text={t('trashed_projects')} />
{withHr && (
{hasDsNav && (
<li role="none">
<hr />
</li>
@@ -2,6 +2,7 @@
@import 'cms';
@import 'content';
@import 'project-list';
@import 'project-list-default';
@import 'project-list-ds-nav';
@import 'sidebar-v2-dash-pane';
@import 'editor/ide';
@@ -0,0 +1,140 @@
.project-list-react {
#project-list-root > &.content {
padding-top: $header-height;
padding-bottom: 0;
min-height: calc(100vh - #{$header-height});
display: flex;
flex-direction: column;
}
.project-list-wrapper {
display: flex;
align-items: stretch;
width: 100%;
min-height: calc(100vh - #{$header-height});
}
.project-list-sidebar-wrapper-react {
position: relative;
background-color: var(--bg-dark-secondary);
flex: 0 0 15%;
min-height: calc(100vh - #{$header-height});
max-width: 320px;
min-width: 200px;
.project-list-sidebar-subwrapper {
display: flex;
flex-direction: column;
height: 100%;
.project-list-sidebar-react {
flex-grow: 1;
padding: var(--spacing-08) var(--spacing-06);
-ms-overflow-style: -ms-autohiding-scrollbar;
color: var(--neutral-40);
.small {
color: var(--neutral-40);
}
}
}
}
.project-list-main-react {
flex: 1;
overflow-x: hidden;
padding: var(--spacing-08) var(--spacing-06);
}
ul.project-list-filters {
margin: var(--spacing-05) calc(-1 * var(--spacing-06));
> li {
> button {
width: 100%;
font-weight: normal;
text-align: left;
color: var(--content-primary-dark);
background-color: transparent;
border-radius: unset;
border: none;
border-bottom: solid 1px transparent;
padding: var(--spacing-03) var(--spacing-06);
&:hover {
background-color: var(--neutral-70);
}
&:focus {
text-decoration: none;
outline: none;
}
}
}
> li.active {
> button {
background-color: var(--neutral-90);
color: var(--content-primary-dark);
.subdued {
color: var(--content-primary-dark);
}
}
}
> li.tag {
&.active {
.tag-menu > button {
color: var(--content-primary-dark);
border-color: var(--white);
&:hover {
background-color: var(--neutral-90);
}
}
}
&:hover {
&:not(.active) {
background-color: var(--neutral-70);
}
}
&:not(.active) {
.tag-menu > a:hover {
background-color: var(--neutral-90);
}
}
}
.tag-menu {
button.dropdown-toggle {
border: 1px solid var(--white);
color: var(--content-primary-dark);
}
}
}
.loading-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: calc(100vh - #{$header-height});
.loading-screen-brand-container {
margin: 0 auto;
}
}
.project-list-sidebar-survey-wrapper {
position: sticky;
bottom: 0;
@include media-breakpoint-down(md) {
position: static;
margin-top: var(--spacing-05);
}
}
}
@@ -5,16 +5,31 @@
height: 100dvh;
color: var(--content-secondary);
// NOTE-AC: This code can be eliminated when we remove sidebar-navigation-ui-update
--navbar-btn-padding-h: var(--spacing-06);
--navbar-subdued-padding: calc(
var(--navbar-btn-padding-v) + var(--navbar-btn-border-width)
)
calc(var(--navbar-btn-padding-h) + 1px);
.navbar-default {
position: relative;
.navbar-header {
padding-top: var(--spacing-08);
}
}
.project-ds-nav-sidebar-and-content {
.project-list-wrapper {
flex-grow: 1;
display: flex;
overflow-y: hidden;
.project-ds-nav-sidebar {
.new-project-button {
margin-bottom: var(--spacing-08);
}
.project-list-sidebar-wrapper-react {
position: relative;
display: flex;
flex-direction: column;
@@ -30,35 +45,28 @@
padding: 0 var(--spacing-07);
}
.new-project-button {
margin-bottom: var(--spacing-08);
&::after {
display: none;
}
.project-list-sidebar-survey-link {
color: var(--content-secondary) !important;
}
.dropdown {
width: 100%;
.survey-notification {
background-color: var(--bg-light-secondary);
color: var(--content-secondary);
box-shadow: none;
.new-project-button {
width: 100%;
button.close {
color: var(--content-secondary) !important;
padding: 0;
}
}
}
ul.project-list-filters {
.subdued {
color: var(--content-disabled);
}
hr {
margin: var(--spacing-05) 0;
}
> li {
position: relative;
> button {
width: 100%;
text-align: left;
@@ -67,102 +75,54 @@
border-radius: var(--border-radius-medium);
border: none;
padding: var(--spacing-04) var(--spacing-05);
&:hover {
background-color: var(--bg-light-secondary);
}
}
&.active {
button {
background-color: var(--bg-accent-03);
color: var(--green-60);
font-weight: bold;
}
&:hover button {
background-color: var(--bg-light-secondary);
}
&.active button {
background-color: var(--bg-accent-03);
color: var(--green-60);
font-weight: bold;
}
}
.dropdown-header {
@include body-sm;
padding: var(--spacing-05) var(--spacing-06);
text-transform: uppercase;
font-weight: bold;
}
> li.tag {
&:hover {
.tag-menu {
display: block;
}
}
button.tag-name {
position: relative;
padding-right: var(--spacing-08);
display: flex;
align-items: center;
word-wrap: anywhere;
.tag-list-icon {
vertical-align: sub;
font-weight: bold;
}
span.name {
padding-left: 0.5em;
line-height: 1.4;
}
padding-right: var(--spacing-08) !important;
}
}
.tag-menu {
&.show {
display: block;
}
button.dropdown-toggle {
border: 1px solid var(--content-secondary);
border-radius: var(--border-radius-base);
background-color: transparent;
color: var(--content-secondary);
display: flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
position: relative;
padding: var(--spacing-01) var(--spacing-03);
&::after {
margin: 0;
}
}
}
}
display: none;
width: auto;
position: absolute;
top: 50%;
margin-top: -8px; // Half the element height.
right: 4px;
.project-dash-table {
.btn-link {
color: var(--content-secondary);
height: var(--spacing-08);
width: var(--spacing-08);
border-radius: 100%;
padding: var(--spacing-01) 0 0;
vertical-align: middle;
&.open {
display: block;
&:hover,
&:focus {
background-color: #d9d9d9 !important;
}
}
button.tag-action {
border-radius: unset;
width: 100%;
background-color: transparent;
border-color: transparent;
color: var(--neutral-70);
text-align: left;
font-weight: normal;
&:active {
outline: none;
}
}
.dash-cell-name a {
color: var(--content-secondary) !important;
}
}
@@ -171,84 +131,20 @@
overflow-y: auto;
position: relative;
background-color: var(--bg-light-secondary);
padding: var(--spacing-08);
padding: var(--spacing-08) var(--spacing-06);
@media (width >= 768px) {
@include media-breakpoint-up(md) {
padding: var(--spacing-08);
border-top-left-radius: var(--border-radius-large);
}
}
}
.fat-footer-container {
width: 100% !important;
}
}
.add-affiliation {
.progress {
height: var(--spacing-05);
margin-bottom: var(--spacing-03);
}
p {
margin-bottom: var(--spacing-03);
}
&.is-mobile p {
@include body-xs;
white-space: normal;
}
}
.survey-notification {
display: flex;
flex-wrap: wrap;
padding: var(--spacing-06);
background-color: var(--bg-light-secondary);
border-color: transparent;
color: var(--content-secondary);
border-radius: var(--border-radius-base);
@include media-breakpoint-up(md) {
flex-wrap: nowrap;
}
button.close {
padding: 0;
.fat-footer-container {
width: 100% !important;
}
}
}
.project-list-sidebar-survey-wrapper {
margin-top: var(--spacing-05);
.survey-notification {
font-size: var(--font-size-02);
a {
text-decoration: none;
}
}
.project-list-sidebar-survey-link {
color: var(--content-secondary) !important;
}
@include media-breakpoint-down(md) {
position: static;
margin-top: var(--spacing-05);
.survey-notification {
font-size: unset;
.project-list-sidebar-survey-link {
display: block;
align-items: center;
min-width: 48px;
min-height: 48px;
padding-top: var(--spacing-07);
color: var(--content-secondary) !important;
}
}
}
}
@@ -17,63 +17,24 @@
padding: 0 var(--spacing-02);
}
.project-list-react {
#project-list-root > &.content {
padding-top: $header-height;
padding-bottom: 0;
min-height: calc(100vh - #{$header-height});
display: flex;
flex-direction: column;
}
#project-list-root {
.user-notifications ul {
margin-bottom: 0;
}
.project-list-wrapper {
display: flex;
align-items: stretch;
width: 100%;
min-height: calc(100vh - #{$header-height});
}
.project-list-sidebar-wrapper-react {
position: relative;
background-color: var(--bg-dark-secondary);
flex: 0 0 15%;
min-height: calc(100vh - #{$header-height});
max-width: 320px;
min-width: 200px;
button {
white-space: normal;
word-wrap: anywhere;
.project-list-sidebar-subwrapper {
display: flex;
flex-direction: column;
height: 100%;
// prevents buttons from expanding sidebar width
}
.project-list-sidebar-react {
flex-grow: 1;
padding: var(--spacing-08) var(--spacing-06);
-ms-overflow-style: -ms-autohiding-scrollbar;
color: var(--neutral-40);
.new-project-dropdown {
width: 100%;
.small {
color: var(--neutral-40);
}
button {
white-space: normal;
word-wrap: anywhere;
// prevents buttons from expanding sidebar width
}
> .dropdown {
width: 100%;
.new-project-button {
width: 100%;
}
}
.new-project-button {
width: 100%;
}
}
}
@@ -175,12 +136,6 @@
}
}
.project-list-main-react {
flex: 1;
overflow-x: hidden;
padding: var(--spacing-08) var(--spacing-06);
}
.project-list-header-row {
display: flex;
align-items: center;
@@ -241,8 +196,6 @@
}
ul.project-list-filters {
margin: var(--spacing-05) calc(-1 * var(--spacing-06));
.subdued {
color: var(--content-disabled);
}
@@ -252,24 +205,9 @@
position: relative;
> button {
width: 100%;
font-weight: normal;
text-align: left;
color: var(--content-primary-dark);
background-color: transparent;
border-radius: unset;
border: none;
border-bottom: solid 1px transparent;
padding: var(--spacing-03) var(--spacing-06);
&:hover {
background-color: var(--neutral-70);
text-decoration: none;
}
&:hover,
&:focus {
text-decoration: none;
outline: none;
}
}
@@ -290,13 +228,7 @@
border-radius: 0;
> button {
background-color: var(--neutral-90);
font-weight: 700;
color: var(--content-primary-dark);
.subdued {
color: var(--content-primary-dark);
}
}
}
@@ -316,17 +248,6 @@
}
}
&.active {
.tag-menu > button {
color: var(--content-primary-dark);
border-color: var(--white);
&:hover {
background-color: var(--neutral-90);
}
}
}
&.untagged {
button.tag-name {
span.name {
@@ -337,21 +258,11 @@
}
&:hover {
&:not(.active) {
background-color: var(--neutral-70);
}
.tag-menu {
display: block;
}
}
&:not(.active) {
.tag-menu > a:hover {
background-color: var(--neutral-90);
}
}
button.tag-name {
position: relative;
padding: var(--spacing-03) var(--spacing-09) var(--spacing-03)
@@ -374,10 +285,8 @@
.tag-menu {
button.dropdown-toggle {
border: 1px solid var(--white);
border-radius: var(--border-radius-base);
background-color: transparent;
color: var(--content-primary-dark);
display: flex;
align-items: center;
justify-content: center;
@@ -682,57 +591,6 @@
margin: 0 auto;
}
}
.survey-notification {
display: flex;
flex-wrap: wrap;
padding: var(--spacing-06);
background-color: var(--bg-dark-tertiary);
border-color: transparent;
color: var(--neutral-20);
box-shadow: 2px 4px 6px rgb(0 0 0 / 25%);
border-radius: var(--border-radius-base);
@include media-breakpoint-up(md) {
flex-wrap: nowrap;
}
button.close {
@extend .text-white;
padding: 0;
}
}
.project-list-sidebar-survey-wrapper {
position: sticky;
bottom: 0;
.survey-notification {
font-size: var(--font-size-02);
a {
text-decoration: none;
}
}
@include media-breakpoint-down(md) {
position: static;
margin-top: var(--spacing-05);
.survey-notification {
font-size: unset;
.project-list-sidebar-survey-link {
display: block;
align-items: center;
min-width: 48px;
min-height: 48px;
padding-top: var(--spacing-07);
}
}
}
}
}
.current-plan {
@@ -765,6 +623,51 @@
}
}
.survey-notification {
display: flex;
flex-wrap: wrap;
padding: var(--spacing-06);
background-color: var(--bg-dark-tertiary);
border-color: transparent;
color: var(--neutral-20);
box-shadow: 2px 4px 6px rgb(0 0 0 / 25%);
border-radius: var(--border-radius-base);
@include media-breakpoint-up(md) {
flex-wrap: nowrap;
}
button.close {
@extend .text-white;
padding: 0;
}
}
.project-list-sidebar-survey-wrapper {
.survey-notification {
font-size: var(--font-size-02);
a {
text-decoration: none;
}
}
@include media-breakpoint-down(md) {
.survey-notification {
font-size: unset;
.project-list-sidebar-survey-link {
display: block;
align-items: center;
min-width: 48px;
min-height: 48px;
padding-top: var(--spacing-07);
}
}
}
}
.project-list-load-more-button {
margin-bottom: var(--spacing-05);
}