Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hover buttons to recipe cards #1577

Merged
merged 10 commits into from
May 10, 2024
48 changes: 35 additions & 13 deletions fronts-client/src/components/card/article/ArticleBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { ImageMetadataContainer } from 'components/image/ImageMetaDataContainer'
import EditModeVisibility from 'components/util/EditModeVisibility';
import PageViewDataWrapper from '../../PageViewDataWrapper';
import ImageAndGraphWrapper from 'components/image/ImageAndGraphWrapper';
import { getPaths } from 'util/paths';

const ThumbnailPlaceholder = styled(BasePlaceholder)`
flex-shrink: 0;
Expand Down Expand Up @@ -178,6 +179,7 @@ const articleBodyDefault = React.memo(
}: ArticleBodyProps) => {
const displayByline = size === 'default' && showByline && byline;
const now = Date.now();
const paths = urlPath ? getPaths(urlPath) : undefined;

return (
<>
Expand Down Expand Up @@ -321,21 +323,41 @@ const articleBodyDefault = React.memo(
</ImageAndGraphWrapper>
<HoverActionsAreaOverlay disabled={isUneditable}>
<HoverActionsButtonWrapper
buttons={[
{ text: 'View', component: HoverViewButton },
{ text: 'Ophan', component: HoverOphanButton },
{ text: 'Clipboard', component: HoverAddToClipboardButton },
{ text: 'Delete', component: HoverDeleteButton },
]}
buttonProps={{
isLive,
urlPath,
onDelete,
onAddToClipboard,
}}
size={size}
toolTipPosition={'top'}
toolTipAlign={'left'}
toolTipAlign={'right'}
renderButtons={(props) => (
<>
{urlPath && (
<HoverViewButton
hoverText="View"
href={isLive ? paths?.live : paths?.preview}
{...props}
/>
)}
{isLive && (
<HoverOphanButton
{...props}
href={paths?.ophan}
hoverText="Ophan"
/>
)}
{onAddToClipboard && (
<HoverAddToClipboardButton
onAddToClipboard={onAddToClipboard}
hoverText="Clipboard"
{...props}
/>
)}
{onDelete && (
<HoverDeleteButton
onDelete={onDelete}
hoverText="Delete"
{...props}
/>
)}
</>
)}
/>
</HoverActionsAreaOverlay>
</>
Expand Down
40 changes: 38 additions & 2 deletions fronts-client/src/components/card/recipe/RecipeCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@ import { ThumbnailSmall } from 'components/image/Thumbnail';
import CardMetaContent from '../CardMetaContent';
import { upperFirst } from 'lodash';
import { useSelector } from 'react-redux';
import { HoverActionsAreaOverlay } from 'components/CollectionHoverItems';
import { HoverActionsButtonWrapper } from 'components/inputs/HoverActionButtonWrapper';
import {
HoverAddToClipboardButton,
HoverDeleteButton,
HoverViewButton,
} from 'components/inputs/HoverActionButtons';
import { getPaths } from 'util/paths';

interface Props {
onDragStart?: (d: React.DragEvent<HTMLElement>) => void;
onDrop?: (d: React.DragEvent<HTMLElement>) => void;
onDelete?: (uuid: string) => void;
onAddToClipboard?: (uuid: string) => void;
onDelete: () => void;
onAddToClipboard: () => void;
onClick?: () => void;
id: string;
collectionId?: string;
Expand Down Expand Up @@ -53,6 +61,9 @@ export const RecipeCard = ({
const recipe = useSelector((state) =>
recipeSelectors.selectById(state, card.id)
);
const paths = recipe?.canonicalArticle
? getPaths(recipe.canonicalArticle)
: undefined;

return (
<CardContainer {...rest}>
Expand Down Expand Up @@ -82,6 +93,31 @@ export const RecipeCard = ({
<ImageAndGraphWrapper size={size}>
<ThumbnailSmall url={recipe?.featuredImage.url} />
</ImageAndGraphWrapper>
<HoverActionsAreaOverlay data-testid="hover-overlay">
<HoverActionsButtonWrapper
toolTipPosition={'top'}
toolTipAlign={'right'}
renderButtons={(props) => (
<>
<HoverViewButton
hoverText="View"
href={paths?.live}
{...props}
/>
<HoverAddToClipboardButton
onAddToClipboard={onAddToClipboard}
hoverText="Clipboard"
{...props}
/>
<HoverDeleteButton
hoverText="Delete"
onDelete={onDelete}
{...props}
/>
</>
)}
/>
</HoverActionsAreaOverlay>
</CardBody>
</CardContainer>
);
Expand Down
40 changes: 25 additions & 15 deletions fronts-client/src/components/card/snapLink/SnapLinkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { selectFeatureValue } from 'selectors/featureSwitchesSelectors';
import ImageAndGraphWrapper from 'components/image/ImageAndGraphWrapper';
import { ThumbnailCutout } from 'components/image/Thumbnail';
import PageViewDataWrapper from 'components/PageViewDataWrapper';
import { getPathsForSnap } from 'util/paths';

const SnapLinkBodyContainer = styled(CardBody)`
justify-content: space-between;
Expand All @@ -54,8 +55,8 @@ const SnapLinkURL = styled.p`
interface ContainerProps {
onDragStart?: (d: React.DragEvent<HTMLElement>) => void;
onDrop?: (d: React.DragEvent<HTMLElement>) => void;
onDelete?: (uuid: string) => void;
onAddToClipboard?: (uuid: string) => void;
onDelete: () => void;
onAddToClipboard: () => void;
onClick?: () => void;
id: string;
collectionId?: string;
Expand Down Expand Up @@ -192,22 +193,31 @@ const SnapLinkCard = ({
justify={'space-between'}
>
<HoverActionsButtonWrapper
buttons={[
{ text: 'View', component: HoverViewButton },
{ text: 'Ophan', component: HoverOphanButton },
{ text: 'Clipboard', component: HoverAddToClipboardButton },
{ text: 'Delete', component: HoverDeleteButton },
]}
buttonProps={{
isLive: true, // it should not be possible for a snap link to be anything other than live?
urlPath,
onAddToClipboard,
onDelete,
isSnapLink: true,
}}
size={size}
toolTipPosition={'top'}
toolTipAlign={'right'}
renderButtons={(props) => (
<>
{urlPath && (
<HoverViewButton hoverText="View" href={urlPath} {...props} />
)}
<HoverOphanButton
{...props}
hoverText="Ophan"
href={urlPath && getPathsForSnap(urlPath).ophan}
/>
<HoverAddToClipboardButton
onAddToClipboard={onAddToClipboard}
hoverText="Clipboard"
{...props}
/>
<HoverDeleteButton
onDelete={onDelete}
hoverText="Delete"
{...props}
/>
</>
)}
/>
</HoverActionsAreaOverlay>
</SnapLinkBodyContainer>
Expand Down
2 changes: 2 additions & 0 deletions fronts-client/src/components/feed/ArticleFeedItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { insertCardWithCreate } from '../../actions/Cards';
import { connect } from 'react-redux';
import { FeedItem } from './FeedItem';
import { ContentInfo } from './ContentInfo';
import { CardTypesMap } from 'constants/cardTypes';

const Tone = styled.span`
font-weight: normal;
Expand Down Expand Up @@ -56,6 +57,7 @@ const ArticleFeedItemComponent = ({

return (
<FeedItem
type={CardTypesMap.ARTICLE}
id={article.id}
title={article.webTitle}
liveUrl={getPaths(article.id).live}
Expand Down
2 changes: 2 additions & 0 deletions fronts-client/src/components/feed/ChefFeedItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { selectFeatureValue } from '../../selectors/featureSwitchesSelectors';
import { State } from '../../types/State';
import { connect } from 'react-redux';
import noop from 'lodash/noop';
import { CardTypesMap } from 'constants/cardTypes';

interface ComponentProps {
chef: Chef;
Expand All @@ -33,6 +34,7 @@ export const ChefFeedItemComponent = ({
return (
<FeedItem
id={chef.id}
type={CardTypesMap.CHEF}
title={`${chef.firstName} ${chef.lastName}`}
hasVideo={false}
isLive={true}
Expand Down
31 changes: 21 additions & 10 deletions fronts-client/src/components/feed/FeedItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import CircularIconContainer from 'components/icons/CircularIconContainer';
import RefreshPeriodically from '../util/RefreshPeriodically';
import { collectionArticlesPollInterval } from 'constants/polling';
import RenderOffscreen from 'components/util/RenderOffscreen';
import { getPaths } from 'util/paths';
import { CardTypes, CardTypesMap } from 'constants/cardTypes';

const Container = styled.div`
display: flex;
Expand Down Expand Up @@ -98,6 +100,7 @@ const VideoIconContainer = styled(CircularIconContainer)`

interface FeedItemProps {
id: string;
type: CardTypes;
title: string;
liveUrl?: string;
metaContent?: JSX.Element;
Expand All @@ -123,6 +126,7 @@ export class FeedItem extends React.Component<FeedItemProps, {}> {
public render() {
const {
id,
type,
title,
liveUrl,
isLive,
Expand All @@ -136,6 +140,10 @@ export class FeedItem extends React.Component<FeedItemProps, {}> {
handleDragStart,
} = this.props;

const { preview, live, ophan } = getPaths(id);
const href = isLive ? live : preview;
const displayOphanLink = type === CardTypesMap.ARTICLE && isLive;

return (
<Container
data-testid="feed-item"
Expand Down Expand Up @@ -196,18 +204,21 @@ export class FeedItem extends React.Component<FeedItemProps, {}> {
</FeedItemContainer>
<HoverActionsAreaOverlay data-testid="hover-overlay">
<HoverActionsButtonWrapper
buttons={[
{ text: 'View', component: HoverViewButton },
{ text: 'Ophan', component: HoverOphanButton },
{ text: 'Clipboard', component: HoverAddToClipboardButton },
]}
buttonProps={{
isLive,
urlPath: id,
onAddToClipboard,
}}
toolTipPosition={'top'}
toolTipAlign={'right'}
renderButtons={(props) => (
<>
<HoverViewButton hoverText="View" href={href} {...props} />
{displayOphanLink && (
<HoverOphanButton {...props} href={ophan} hoverText="Ophan" />
)}
<HoverAddToClipboardButton
onAddToClipboard={onAddToClipboard}
hoverText="Clipboard"
{...props}
/>
</>
)}
/>
</HoverActionsAreaOverlay>
</Container>
Expand Down
41 changes: 25 additions & 16 deletions fronts-client/src/components/feed/RecipeFeedItem.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,39 @@
import { Recipe } from '../../types/Recipe';
import { FeedItem } from './FeedItem';
import React from 'react';
import React, { useCallback } from 'react';
import { State } from '../../types/State';
import {
dragOffsetX,
dragOffsetY,
} from '../FrontsEdit/CollectionComponents/ArticleDrag';
import noop from 'lodash/noop';
import { selectFeatureValue } from '../../selectors/featureSwitchesSelectors';
import { connect } from 'react-redux';
import { ContentInfo } from './ContentInfo';
import { CardTypesMap } from 'constants/cardTypes';
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import { insertCardWithCreate } from 'actions/Cards';

interface ComponentProps {
recipe: Recipe;
shouldObscureFeed: boolean;
}

export const RecipeFeedItemComponent = ({
recipe,
shouldObscureFeed,
}: ComponentProps) => {
export const RecipeFeedItem = ({ recipe }: ComponentProps) => {
const shouldObscureFeed = useSelector<State, boolean>((state) =>
selectFeatureValue(state, 'obscure-feed')
);

const dispatch = useDispatch();

const onAddToClipboard = useCallback(() => {
dispatch<any>(
insertCardWithCreate(
{ type: 'clipboard', id: 'clipboard', index: 0 },
{ type: 'RECIPE', data: recipe },
'clipboard'
)
);
}, [recipe]);

const handleDragStart = (
event: React.DragEvent<HTMLDivElement>,
dragNode: HTMLDivElement
Expand All @@ -32,22 +46,17 @@ export const RecipeFeedItemComponent = ({

return (
<FeedItem
id={recipe.id}
type={CardTypesMap.RECIPE}
id={recipe.canonicalArticle}
title={recipe.title}
thumbnail={recipe.featuredImage.url}
liveUrl={`https://theguardian.com/${recipe.canonicalArticle}`}
hasVideo={false}
isLive={true} // We do not yet serve preview recipes
handleDragStart={handleDragStart}
onAddToClipboard={noop}
onAddToClipboard={onAddToClipboard}
shouldObscureFeed={shouldObscureFeed}
metaContent={<ContentInfo>Recipe</ContentInfo>}
/>
);
};

const mapStateToProps = (state: State) => ({
shouldObscureFeed: selectFeatureValue(state, 'obscure-feed'),
});

export const RecipeFeedItem = connect(mapStateToProps)(RecipeFeedItemComponent);
Loading