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

Share button #10422

Merged
merged 38 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
73950f6
share button
DanielCliftonGuardian Jan 31, 2024
230d991
renaming
DanielCliftonGuardian Jan 31, 2024
f41fcaa
Update ShareButton.importable.tsx
DanielCliftonGuardian Jan 31, 2024
dae8f9d
Add useEffect
DanielCliftonGuardian Feb 1, 2024
eecb087
Merge branch 'main' into share-button
DanielCliftonGuardian Feb 8, 2024
989699c
Add new styles
DanielCliftonGuardian Feb 8, 2024
b12ac3d
Add stories
DanielCliftonGuardian Feb 9, 2024
588a8ca
Add to palette
DanielCliftonGuardian Feb 9, 2024
98c473e
fix add story
DanielCliftonGuardian Feb 12, 2024
77cb8fb
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 12, 2024
a80557e
Refactors
DanielCliftonGuardian Feb 13, 2024
e499917
Update NewsletterSignupLayout.tsx
DanielCliftonGuardian Feb 13, 2024
2b0e092
update island defer until visible
DanielCliftonGuardian Feb 13, 2024
84ae3a1
Refactor
DanielCliftonGuardian Feb 13, 2024
ab2f4d3
Update link-icon.svg
DanielCliftonGuardian Feb 13, 2024
5758113
Fix liveblogmobile copyLinkButton styles
DanielCliftonGuardian Feb 13, 2024
2c89049
update fill colour
DanielCliftonGuardian Feb 14, 2024
81976dd
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 14, 2024
505d324
Refactors
DanielCliftonGuardian Feb 14, 2024
81fd81d
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 14, 2024
759f0f4
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 14, 2024
0925622
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 14, 2024
06735fa
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 14, 2024
8213c81
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 14, 2024
ee27ea3
Improvements
DanielCliftonGuardian Feb 19, 2024
c35e66f
Merge branch 'main' into share-button
DanielCliftonGuardian Feb 19, 2024
88fbf5a
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 20, 2024
f89edea
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 20, 2024
45d38aa
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 20, 2024
50d55d4
Update ShareButton.importable.tsx
DanielCliftonGuardian Feb 20, 2024
a5f38bc
Add email link button
DanielCliftonGuardian Feb 21, 2024
fe45066
Fix fall back href
DanielCliftonGuardian Feb 21, 2024
4030d46
Update ShareButton.stories.tsx
DanielCliftonGuardian Feb 21, 2024
c6761e1
Switch to linkButton
DanielCliftonGuardian Feb 21, 2024
efe9779
Update Island.test.tsx
DanielCliftonGuardian Feb 21, 2024
69a12b7
Merge branch 'main' into share-button
DanielCliftonGuardian Feb 21, 2024
a88e86c
Update Island.test.tsx
DanielCliftonGuardian Feb 21, 2024
24cf0ea
Merge branch 'share-button' of https://github.com/guardian/dotcom-ren…
DanielCliftonGuardian Feb 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dotcom-rendering/playwright/fixtures/tweet-block.js

Large diffs are not rendered by default.

20 changes: 11 additions & 9 deletions dotcom-rendering/src/components/ArticleMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { Contributor } from './Contributor';
import { Dateline } from './Dateline';
import { Island } from './Island';
import { SendAMessage } from './SendAMessage.importable';
import { ShareIcons } from './ShareIcons';
import { ShareButton } from './ShareButton.importable';

type Props = {
format: ArticleFormat;
Expand Down Expand Up @@ -415,14 +415,16 @@ export const ArticleMeta = ({
),
]}
>
<ShareIcons
pageId={pageId}
webTitle={webTitle}
format={format}
displayIcons={['facebook', 'twitter', 'email']}
size="medium"
context="ArticleMeta"
/>
<Island
priority="feature"
defer={{ until: 'idle' }}
>
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
<ShareButton
pageId={pageId}
webTitle={webTitle}
format={format}
/>
</Island>
</div>
)}
<div
Expand Down
21 changes: 11 additions & 10 deletions dotcom-rendering/src/components/LiveBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { isUndefined } from '@guardian/libs';
import type { EditionId } from '../lib/edition';
import { RenderArticleElement } from '../lib/renderElement';
import type { ServerSideTests, Switches } from '../types/config';
import { Island } from './Island';
import { LastUpdated } from './LastUpdated';
import { LiveBlockContainer } from './LiveBlockContainer';
import { ShareIcons } from './ShareIcons';
import { ShareButton } from './ShareButton.importable';

type Props = {
format: ArticleFormat;
Expand Down Expand Up @@ -93,15 +94,15 @@ export const LiveBlock = ({
justify-content: space-between;
`}
>
<ShareIcons
pageId={pageId}
blockId={block.id}
webTitle={webTitle}
displayIcons={['facebook', 'twitter']}
format={format}
size="small"
context="LiveBlock"
/>
<Island priority="feature" defer={{ until: 'idle' }}>
<ShareButton
size="xsmall"
pageId={pageId}
blockId={block.id}
webTitle={webTitle}
format={format}
/>
</Island>
{!isUndefined(lastUpdated) && (
<LastUpdated
lastUpdated={lastUpdated}
Expand Down
223 changes: 223 additions & 0 deletions dotcom-rendering/src/components/ShareButton.importable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import { css } from '@emotion/react';
import { palette, until } from '@guardian/source-foundations';
import type { Size } from '@guardian/source-react-components';
import {
Button,
SvgCheckmark,
SvgShare,
} from '@guardian/source-react-components';
import { useEffect, useState } from 'react';
import { palette as themePalette } from '../palette';
import LinkIcon from '../static/icons/link-icon.svg';

type Props = {
size?: Size | undefined;
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
pageId: string;
blockId?: string;
webTitle: string;
format: ArticleFormat;
};

const sharedButtonStyles = (sizeXSmall: boolean) => css`
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
border-color: ${themePalette('--share-button-border')};
min-width: ${sizeXSmall ? '101px' : '132px'};
max-width: ${sizeXSmall ? '101px' : '132px'};
font-size: ${sizeXSmall ? '14px' : '17px'};
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
svg {
width: ${sizeXSmall ? '13.33px' : '16.67px'};
height: ${sizeXSmall ? '13.33px' : '16.67px'};
}
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
`;

const buttonStyles = css`
color: ${themePalette('--share-button')};
svg {
fill: ${themePalette('--share-button')};
margin-left: 0;
}
:hover {
background-color: ${themePalette('--share-button')};
border-color: ${themePalette('--share-button')};
color: ${themePalette('--share-button-hover')};
svg {
fill: ${themePalette('--share-button-hover')};
}
}
`;

const copiedButtonStyles = css`
color: ${themePalette('--share-button-copied')};
svg {
fill: ${palette.success[400]};
}
`;

const nativeShare = (sizeXSmall: boolean) => css`
min-width: ${sizeXSmall ? '79px' : '105px'};
max-width: ${sizeXSmall ? '79px' : '105px'};
svg {
margin-right: 6px;
width: 18.33px;
height: 18.33px;
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
}
border-color: ${themePalette('--share-button-border')};
`;

const liveBlogMobile = css`
${until.desktop} {
color: ${palette.neutral[100]};
border-color: rgba(255, 255, 255, 0.4);
svg {
fill: ${palette.neutral[100]};
margin-left: 0;
}
:hover {
color: ${themePalette('--share-button-liveblog-mobile')};
background-color: ${palette.neutral[100]};
svg {
fill: ${themePalette('--share-button-liveblog-mobile')};
}
}
}
`;

const getUrl = ({
pageId,
CMP,
blockId,
}: {
pageId: string;
CMP?: string;
blockId?: string;
}) => {
const searchParams = new URLSearchParams({});
if (CMP) searchParams.append('CMP', CMP); // Do we want to track this?
if (blockId) searchParams.append('page', `with:block-${blockId}`);

const blockHash = blockId ? `#block-${blockId}` : '';
const paramsString = searchParams.toString();
return new URL(
`${pageId}${paramsString ? '?' + paramsString : ''}${blockHash}`,
'https://www.theguardian.com/',
).href;
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
};

export const NativeShareButton = ({
onShare,
size,
isLiveBlogMeta,
}: {
onShare: () => Promise<void>;
size?: Size | undefined;
isLiveBlogMeta: boolean;
}) => {
const sizeXSmall = size === 'xsmall';

return (
<Button
onClick={() => onShare().catch(() => {})}
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
size={size}
type="button"
priority="tertiary"
iconSide="left"
icon={<SvgShare />}
css={[
buttonStyles,
nativeShare(sizeXSmall),
isLiveBlogMeta && liveBlogMobile,
]}
>
Share
</Button>
);
};

export const CopyLinkButton = ({
onShare,
size,
isLiveBlogMeta,
isCopied,
}: {
onShare: () => Promise<void>;
size?: Size | undefined;
isLiveBlogMeta: boolean;
isCopied: boolean;
}) => {
const sizeXSmall = size === 'xsmall';
return (
<Button
onClick={() => onShare().catch(() => {})}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do something in this function?

size={size}
type="button"
priority="tertiary"
iconSide="left"
icon={isCopied ? <SvgCheckmark /> : <LinkIcon />}
css={
isCopied
? [copiedButtonStyles, sharedButtonStyles(sizeXSmall)]
: [
buttonStyles,
sharedButtonStyles(sizeXSmall),
isLiveBlogMeta && liveBlogMobile,
]
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
}
>
{isCopied ? 'Link copied' : 'Copy link'}
</Button>
);
};

export const ShareButton = ({
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
size = 'small',
pageId,
blockId,
webTitle,
format,
}: Props) => {
const [isCopied, setIsCopied] = useState(false);
const [isShareSupported, setIsShareSupported] = useState(false);

const isLiveBlogMeta = format.design === 11 && size !== 'xsmall';
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
setIsShareSupported(
typeof navigator !== 'undefined' && 'share' in navigator,
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
);
}, []);

const onShare = async () => {
if (isShareSupported) {
await navigator.share({
title: `${webTitle}`,
text: `${webTitle}: ${getUrl({
pageId,
blockId,
})}`,
DanielCliftonGuardian marked this conversation as resolved.
Show resolved Hide resolved
});
} else if ('clipboard' in navigator) {
await navigator.clipboard.writeText(
getUrl({
pageId,
blockId,
}),
);
setIsCopied(true);
setTimeout(() => setIsCopied(false), 3000);
}
};

return isShareSupported ? (
<NativeShareButton
onShare={onShare}
size={size}
isLiveBlogMeta={isLiveBlogMeta}
/>
) : (
<CopyLinkButton
onShare={onShare}
size={size}
isCopied={isCopied}
isLiveBlogMeta={isLiveBlogMeta}
/>
);
};
92 changes: 92 additions & 0 deletions dotcom-rendering/src/components/ShareButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { css } from '@emotion/react';
import { ArticleDesign, ArticleDisplay, Pillar } from '@guardian/libs';
import type { StoryProps } from '../../.storybook/decorators/splitThemeDecorator';
import { splitTheme } from '../../.storybook/decorators/splitThemeDecorator';
import { palette as themePalette } from '../palette';
import {
CopyLinkButton,
NativeShareButton,
ShareButton,
} from './ShareButton.importable';

export default {
component: ShareButton,
title: 'Components/ShareButton',
};

interface StoryArgs extends StoryProps {
theme: string;
}

export const CopyLink = () => {
return (
<ShareButton
pageId={'123'}
webTitle={'The the'}
format={{
display: ArticleDisplay.Standard,
theme: Pillar.News,
design: ArticleDesign.Standard,
}}
/>
);
};
CopyLink.storyName = 'CopyLink';
CopyLink.decorators = [splitTheme()];

export const CopyLinkXSmall = () => {
return (
<ShareButton
pageId={'123'}
webTitle={'The the'}
size="xsmall"
format={{
display: ArticleDisplay.Standard,
theme: Pillar.News,
design: ArticleDesign.Standard,
}}
/>
);
};
CopyLinkXSmall.storyName = 'CopyLinkXSmall';
CopyLinkXSmall.decorators = [splitTheme()];

export const LinkCopied = () => {
return (
<CopyLinkButton
onShare={async () => {}}
isCopied={true}
isLiveBlogMeta={true}
/>
);
};
LinkCopied.storyName = 'LinkCopied';
LinkCopied.decorators = [splitTheme()];

export const NativeShare = () => {
return <NativeShareButton onShare={async () => {}} isLiveBlogMeta={true} />;
};
NativeShare.storyName = 'NativeShare';
NativeShare.decorators = [splitTheme()];

export const LiveBlogMobile = ({ theme }: StoryArgs) => {
return (
<div
css={css`
background-color: ${theme === 'light'
? themePalette('--share-button-liveblog-mobile')
: 'inherit'};
`}
>
<NativeShareButton onShare={async () => {}} isLiveBlogMeta={true} />
</div>
);
};
LiveBlogMobile.storyName = 'LiveBlogMobile';
LiveBlogMobile.decorators = [splitTheme()];
LiveBlogMobile.story = {
parameters: {
viewport: { defaultViewport: 'mobileMedium' },
chromatic: { viewports: [375] },
},
};
Loading
Loading