Skip to content

Commit

Permalink
Reposition panels in a single-column translate view (#3038)
Browse files Browse the repository at this point in the history
Details:
* Only show one Helpers panel at the same time
* Use dot rather than count as the tab indicator
* Make visual distinction between the History panel and the Helpers.
* Add support for batch actions in the single-columm UI

Also included:
* Move OriginalString component and friends out of Metadata component
* Unify Term tab count behaviour with other Helper tabs: do not show 0
* Unify dark-theme.css and light-theme.css structure
* De-hardcode and fix color value in the FiltersPanel
  • Loading branch information
mathjazz committed Dec 5, 2023
1 parent 2c75e7f commit d0d0e5f
Show file tree
Hide file tree
Showing 17 changed files with 386 additions and 122 deletions.
10 changes: 5 additions & 5 deletions pontoon/base/static/css/dark-theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@
--popup-background-2: #272a2f;
--icon-background-2: #4d5967;

/* Homepage */
--homepage-background-image: url(../img/background.svg);
--homepage-tour-button-background: #ffffff;
--homepage-tour-button-color: #000000;

/* Tooltip */
--tooltip-background: #000000dd;
--tooltip-color: #ffffff;
--tooltip-color-2: #888888;
--tooltip-border: #4d5967;

/* Homepage */
--homepage-background-image: url(../img/background.svg);
--homepage-tour-button-background: #ffffff;
--homepage-tour-button-color: #000000;

/* Translation status */
--status-translated: #7bc876;
--status-translated-alt: #7bc876;
Expand Down
10 changes: 10 additions & 0 deletions translate/public/locale/en-US/translate.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,16 @@ entitydetails-PluralString--singular = SINGULAR
entitieslist-Entity--sibling-strings-title =
.title = Click to reveal sibling strings
entitieslist-EntitiesList--clear-selected = <glyph></glyph>CLEAR
.title = Uncheck selected strings
entitieslist-EntitiesList--edit-selected =
EDIT <stress>{ $count }</stress> { $count ->
[one] STRING
*[other] STRINGS
}<glyph></glyph>
.title = Edit Selected Strings
## Translation Form

Expand Down
27 changes: 27 additions & 0 deletions translate/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,31 @@
#app .entity-navigation button.previous {
margin-right: 15px;
}

/* History */
#app .entity-details .history {
background: var(--editor-menu-background);
}

/* Helpers */
#app > .main-content .third-column {
border-left: none;
}

#app > .main-content .third-column .react-tabs span.count {
background: var(--light-grey-6);
box-shadow: 0 0 5px;
color: var(--light-grey-6); /* used as box-shadow color */
font-size: 0;
height: 4px;
width: 4px;
padding: 0;
position: absolute;
}

#app > .main-content .third-column .react-tabs span.count:has(.preferred),
#app > .main-content .third-column .react-tabs span.count:has(.pinned) {
background: var(--status-translated-alt);
color: var(--status-translated-alt); /* used as box-shadow color */
}
}
21 changes: 21 additions & 0 deletions translate/src/hooks/useNarrowScreen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect, useState } from 'react';

const NARROW_SCREEN_MAX_WIDTH = 600;

/**
* Return true if the screen is narrower than 600px. Useful in Responsive Web Design.
*/
export function useNarrowScreen(): boolean {
const [isNarrow, setIsNarrow] = useState(
window.innerWidth <= NARROW_SCREEN_MAX_WIDTH,
);

useEffect(() => {
const handleWindowResize = () =>
setIsNarrow(window.innerWidth <= NARROW_SCREEN_MAX_WIDTH);
window.addEventListener('resize', handleWindowResize);
return () => window.removeEventListener('resize', handleWindowResize);
}, []);

return isNarrow;
}
42 changes: 42 additions & 0 deletions translate/src/modules/entitieslist/components/EntitiesList.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,45 @@
font-size: 128px;
margin-bottom: 20px;
}

.entities .toolbar {
position: sticky;
bottom: 0;
background: var(--tooltip-background);
border-top: 1px solid var(--light-grey-1);
box-sizing: border-box;
line-height: 23px;
padding: 10px 12px;
width: 100%;
height: auto;
}

.entities .toolbar button {
background: none;
border: none;
color: var(--tooltip-color-2);
font-weight: 300;
}

.entities .toolbar .clear-selected {
float: left;
}

.entities .toolbar .clear-selected .fa {
padding-right: 6px;
}

.entities .toolbar .edit-selected {
float: right;
text-align: right;
}

.entities .toolbar .edit-selected .fa {
padding-left: 6px;
}

.entities .toolbar .clear-selected:hover,
.entities .toolbar .edit-selected:hover,
.entities .toolbar .edit-selected .selected-count {
color: var(--status-translated);
}
65 changes: 62 additions & 3 deletions translate/src/modules/entitieslist/components/EntitiesList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Localized } from '@fluent/react';
import React, { useCallback, useContext, useEffect, useRef } from 'react';
import useInfiniteScroll from 'react-infinite-scroll-hook';

import type { Entity as EntityType } from '~/api/entity';
import { EntitiesList as EntitiesListContext } from '~/context/EntitiesList';
import { Locale } from '~/context/Locale';
import { Location } from '~/context/Location';
import {
Expand All @@ -13,6 +15,7 @@ import { useEntities } from '~/modules/entities/hooks';
import { SkeletonLoader } from '~/modules/loaders';
import { ENTITY_NOT_FOUND } from '~/modules/notification/messages';
import { useAppDispatch, useAppSelector, useAppStore } from '~/hooks';
import { useNarrowScreen } from '~/hooks/useNarrowScreen';
import { usePrevious } from '~/hooks/usePrevious';
import {
checkSelection,
Expand All @@ -29,6 +32,51 @@ import { Entity } from './Entity';
import { USER } from '~/modules/user';
import { ShowNotification } from '~/context/Notification';

const EntitiesToolbar = ({
count,
onEdit,
onClear,
}: {
count: number;
onEdit: () => void;
onClear: () => void;
}) => (
<div className='toolbar clearfix'>
<Localized
id='entitieslist-EntitiesList--clear-selected'
attrs={{ title: true }}
elems={{
glyph: <i className='fa fa-times fa-lg' />,
}}
>
<button
title='Uncheck selected strings'
onClick={onClear}
className='clear-selected'
>
{'<glyph></glyph> CLEAR'}
</button>
</Localized>
<Localized
id='entitieslist-EntitiesList--edit-selected'
attrs={{ title: true }}
elems={{
glyph: <i className='fa fa-chevron-right fa-lg' />,
stress: <span className='selected-count' />,
}}
vars={{ count }}
>
<button
title='Edit Selected Strings'
onClick={onEdit}
className='edit-selected'
>
{'EDIT <stress>{ $count }</stress> STRINGS<glyph></glyph>'}
</button>
</Localized>
</div>
);

/**
* Displays a list of entities and their current translation.
*
Expand Down Expand Up @@ -224,6 +272,12 @@ export function EntitiesList(): React.ReactElement<'div'> {
);
}

const selectedEntitiesCount = batchactions.entities.length;
const isNarrowScreen = useNarrowScreen();
const entitiesList = useContext(EntitiesListContext);
const quitBatchActions = useCallback(() => dispatch(resetSelection()), []);
const showBatchActions = useCallback(() => entitiesList.show(false), []);

return (
<div className='entities unselectable' ref={list}>
<ul>
Expand All @@ -234,16 +288,21 @@ export function EntitiesList(): React.ReactElement<'div'> {
toggleForBatchEditing={toggleForBatchEditing}
entity={entity}
isReadOnlyEditor={entity.readonly || !isAuthUser}
selected={
!batchactions.entities.length && entity.pk === location.entity
}
selected={!selectedEntitiesCount && entity.pk === location.entity}
selectEntity={selectEntity}
getSiblingEntities={getSiblingEntities_}
parameters={location}
/>
))}
</ul>
{hasNextPage && <SkeletonLoader items={entities} sentryRef={sentryRef} />}
{selectedEntitiesCount === 0 || !isNarrowScreen ? null : (
<EntitiesToolbar
count={selectedEntitiesCount}
onEdit={showBatchActions}
onClear={quitBatchActions}
/>
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.metadata .source-string-comment .context-issue-button {
.context-issue-button {
background: var(--dark-grey-1);
border: 1px solid var(--dark-grey-2);
border-radius: 4px;
Expand All @@ -11,6 +11,6 @@
padding: 2px 4px;
}

.metadata .source-string-comment .context-issue-button:hover {
.context-issue-button:hover {
border-color: var(--translation-border);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,10 @@ type Props = {

export function ContextIssueButton(props: Props): React.ReactElement<'div'> {
return (
<div className='source-string-comment'>
<Localized id='entitydetails-ContextIssueButton--context-issue-button'>
<button
className='context-issue-button'
onClick={props.openTeamComments}
>
{'REQUEST CONTEXT or REPORT ISSUE'}
</button>
</Localized>
</div>
<Localized id='entitydetails-ContextIssueButton--context-issue-button'>
<button className='context-issue-button' onClick={props.openTeamComments}>
{'REQUEST CONTEXT or REPORT ISSUE'}
</button>
</Localized>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
width: 66.67%;
}

.entity-details .main-column .original-string-panel {
border-bottom: 1px solid var(--main-border-1);
padding: 10px;
}

.entity-details .third-column {
width: 33.33%;
border-left: 1px solid var(--main-border-1);
Expand Down
45 changes: 34 additions & 11 deletions translate/src/modules/entitydetails/components/EntityDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import React, {
} from 'react';

import { EntityView, useActiveTranslation } from '~/context/EntityView';
import { Locale } from '~/context/Locale';
import { Location } from '~/context/Location';
import { UnsavedActions } from '~/context/UnsavedChanges';
import { Editor } from '~/modules/editor/components/Editor';
import { OriginalString } from '~/modules/originalstring';
import { TERM } from '~/modules/terms';
import { get as getTerms } from '~/modules/terms/actions';
import { USER } from '~/modules/user';
Expand All @@ -25,10 +27,13 @@ import {
} from '~/modules/teamcomments/actions';
import { getPlainMessage } from '~/utils/message';

import './EntityDetails.css';
import { ContextIssueButton } from './ContextIssueButton';
import { EntityNavigation } from './EntityNavigation';
import { Helpers } from './Helpers';
import { Metadata } from './Metadata';
import { Screenshots } from './Screenshots';

import './EntityDetails.css';

/**
* Component showing details about an entity.
Expand Down Expand Up @@ -88,21 +93,39 @@ export function EntityDetails(): React.ReactElement<'section'> | null {
[dispatch],
);

const { code } = useContext(Locale);

const openTeamComments = useCallback(() => {
const teamCommentsTab = commentTabRef.current;

// FIXME: This is an ugly hack.
// https://github.com/mozilla/pontoon/issues/2300
const index = teamCommentsTab?._reactInternalFiber.index ?? 0;

setCommentTabIndex(index);
setContactPerson(selectedEntity.project.contact.name);
}, [selectedEntity, setCommentTabIndex, setContactPerson]);

const showContextIssueButton =
user.isAuthenticated && selectedEntity.project.contact;

// No content while loading entity data
return selectedEntity.pk === 0 ? null : (
<section className='entity-details'>
<section className='main-column'>
<EntityNavigation />
<Metadata
entity={selectedEntity}
terms={terms}
navigateToPath={navigateToPath}
teamComments={teamComments}
user={user}
commentTabRef={commentTabRef}
setCommentTabIndex={setCommentTabIndex}
setContactPerson={setContactPerson}
/>
<section className='original-string-panel'>
{showContextIssueButton && (
<ContextIssueButton openTeamComments={openTeamComments} />
)}
<Screenshots source={selectedEntity.comment} locale={code} />
<OriginalString navigateToPath={navigateToPath} terms={terms} />
<Metadata
entity={selectedEntity}
navigateToPath={navigateToPath}
teamComments={teamComments}
/>
</section>
<Editor />
<History />
</section>
Expand Down
Loading

0 comments on commit d0d0e5f

Please sign in to comment.