-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: initial draft of social share functionality
- Loading branch information
1 parent
db33a38
commit e55c7cf
Showing
12 changed files
with
382 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
REACT_APP_GEMINI_API_KEY=your_gemini_api_key | ||
REACT_APP_OPENAI_API_KEY=your_chatgbpy_api_key |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import React from 'react'; | ||
|
||
import { Share2Icon, Link2Icon } from '@radix-ui/react-icons'; | ||
import { | ||
FacebookShareButton, | ||
TwitterShareButton, | ||
EmailShareButton, | ||
WhatsappShareButton, | ||
FacebookIcon, | ||
XIcon, | ||
WhatsappIcon, | ||
EmailIcon, | ||
} from 'react-share'; | ||
|
||
import { useBoolean, useCopyToKeyboard, COPY_STATUS } from 'shared/hooks'; | ||
|
||
import { | ||
ShareButton, | ||
Modal, | ||
Heading, | ||
Body, | ||
Subtitle, | ||
CloseButton, | ||
HeadingRow1, | ||
Footer, | ||
LinkWrapper, | ||
NeutralStatus, | ||
SuccessStatus, | ||
FailedStatus, | ||
} from './index.styles'; | ||
|
||
const iconProps = { | ||
size: 40, | ||
borderRadius: 25, | ||
}; | ||
|
||
type SocialSharePropTypes = { | ||
url: string; | ||
title: string; | ||
}; | ||
|
||
const STATUS_NODES = { | ||
[COPY_STATUS.NOT_COPIED]: NeutralStatus, | ||
[COPY_STATUS.COPIED]: SuccessStatus, | ||
[COPY_STATUS.FAILED]: FailedStatus, | ||
}; | ||
|
||
export function SocialShare({ url, title }: SocialSharePropTypes) { | ||
const [open, { toggle }] = useBoolean(); | ||
const { copyText, status, statusText } = useCopyToKeyboard(url); | ||
|
||
const StatusNode = STATUS_NODES[status]; | ||
|
||
return ( | ||
<> | ||
<ShareButton onClick={toggle} className="share-btn"> | ||
<Share2Icon /> | ||
<span>Share</span> | ||
</ShareButton> | ||
|
||
<Modal open={open} onClose={toggle} closeOnClickOutside> | ||
<Heading> | ||
<HeadingRow1> | ||
<p>Share</p> | ||
<CloseButton>Cancel</CloseButton> | ||
</HeadingRow1> | ||
<Subtitle>Images maybe subject to copyright.</Subtitle> | ||
</Heading> | ||
|
||
<Body> | ||
<FacebookShareButton url={url} title={title}> | ||
<FacebookIcon {...iconProps} /> | ||
</FacebookShareButton> | ||
<WhatsappShareButton url={url}> | ||
<WhatsappIcon {...iconProps} /> | ||
</WhatsappShareButton> | ||
<TwitterShareButton url={url} title={title}> | ||
<XIcon {...iconProps} /> | ||
</TwitterShareButton> | ||
<EmailShareButton | ||
url={url} | ||
subject={title} | ||
body="Sent from x10 search engine" | ||
title={title} | ||
> | ||
<EmailIcon {...iconProps} /> | ||
</EmailShareButton> | ||
</Body> | ||
|
||
<Footer onClick={copyText}> | ||
<StatusNode>{statusText}</StatusNode> | ||
<LinkWrapper> | ||
<span>{url}</span> | ||
<Link2Icon role="button" /> | ||
</LinkWrapper> | ||
</Footer> | ||
</Modal> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react'; | ||
|
||
import { StoryFn, Meta } from '@storybook/react'; | ||
|
||
import { SocialShare } from './index.component'; | ||
|
||
export default { | ||
title: 'Components/SocialShare', | ||
component: SocialShare, | ||
} as Meta<typeof SocialShare>; | ||
|
||
export const Primary: StoryFn<typeof SocialShare> = () => ( | ||
<SocialShare title="View Emmanuel Onah Page" url="https://emmanuelonah.com" /> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import styled from 'styled-components'; | ||
|
||
import { ModalWrapper } from 'shared/components'; | ||
|
||
const ShareButton = styled.button` | ||
background-color: #394457; | ||
color: white; | ||
border: none; | ||
align-items: center; | ||
border-radius: 36px; | ||
cursor: pointer; | ||
display: flex; | ||
justify-content: center; | ||
min-height: 30px; | ||
min-width: 150px; | ||
padding: 1rem; | ||
& span { | ||
padding-left: 0.5rem; | ||
} | ||
`; | ||
|
||
const Modal = styled(ModalWrapper)` | ||
position: fix; | ||
bottom: 0; | ||
right: 0; | ||
left: 0; | ||
margin: 0 auto; | ||
background-color: #303134; | ||
border-radius: 10px 10px 0 0; | ||
min-height: 300px; | ||
width: 100%; | ||
`; | ||
|
||
const Heading = styled.div` | ||
color: rgb(232, 234, 237); | ||
border-bottom: 1px solid rgb(60, 64, 67); | ||
`; | ||
|
||
const HeadingRow1 = styled.div` | ||
display: flex; | ||
justify-content: space-between; | ||
padding: 1rem 1rem 0 1rem; | ||
`; | ||
|
||
const Subtitle = styled.p` | ||
color: rgb(154, 160, 166); | ||
font-size: 12px; | ||
padding: 0 1rem 0.5rem 1rem; | ||
`; | ||
|
||
const CloseButton = styled.button` | ||
color: rgb(232, 234, 237); | ||
background-color: transparent; | ||
border: none; | ||
`; | ||
|
||
const Body = styled.div` | ||
padding: 1rem; | ||
display: flex; | ||
justify-content: space-between; | ||
`; | ||
|
||
const Footer = styled.div` | ||
cursor: pointer; | ||
border-top: 1px solid rgb(60, 64, 67); | ||
color: rgb(154, 160, 166); | ||
font-size: 12px; | ||
padding: 1rem; | ||
`; | ||
|
||
const LinkWrapper = styled.div` | ||
border-bottom: 2px solid rgb(60, 64, 67); | ||
display: flex; | ||
align-items: center; | ||
padding-bottom: 0.5rem; | ||
& span { | ||
text-overflow: ellipsis; | ||
padding-right: 0.5rem; | ||
} | ||
`; | ||
|
||
const NeutralStatus = styled.p` | ||
color: white; | ||
`; | ||
|
||
const SuccessStatus = styled.p` | ||
color: green; | ||
`; | ||
|
||
const FailedStatus = styled.p` | ||
color: red; | ||
`; | ||
|
||
export { | ||
ShareButton, | ||
Modal, | ||
Heading, | ||
Body, | ||
Subtitle, | ||
CloseButton, | ||
HeadingRow1, | ||
Footer, | ||
LinkWrapper, | ||
NeutralStatus, | ||
SuccessStatus, | ||
FailedStatus, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './useBoolean'; | ||
export * from './useForceUpdate'; | ||
export * from './useComposeRefs'; | ||
export * from './useCopyToKeyboard'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { renderHook, act } from 'test'; | ||
|
||
import { useCopyToKeyboard, COPY_STATUS } from './useCopyToKeyboard'; | ||
|
||
Object.assign(navigator, { | ||
clipboard: { | ||
writeText: jest.fn(), | ||
}, | ||
}); | ||
|
||
describe('useCopyToKeyboard', () => { | ||
beforeEach(jest.clearAllMocks); | ||
|
||
it('should initially have NOT_COPIED status and corresponding text', () => { | ||
const { result } = renderHook(() => useCopyToKeyboard('Test text')); | ||
|
||
expect(result.current.status).toBe(COPY_STATUS.NOT_COPIED); | ||
expect(result.current.statusText).toBe('Tap to copy link'); | ||
}); | ||
|
||
it('should change status to COPIED and show corresponding text on successful copy', async () => { | ||
(navigator.clipboard.writeText as jest.Mock).mockResolvedValueOnce(null); | ||
const { result } = renderHook(() => useCopyToKeyboard('Test text')); | ||
|
||
await act(async () => { | ||
await result.current.copyText(); | ||
}); | ||
|
||
expect(result.current.status).toBe(COPY_STATUS.COPIED); | ||
expect(result.current.statusText).toBe('Link copied'); | ||
}); | ||
|
||
it('should change status to FAILED and show corresponding text on copy failure', async () => { | ||
(navigator.clipboard.writeText as jest.Mock).mockRejectedValueOnce(new Error('Failed to copy')); | ||
const { result } = renderHook(() => useCopyToKeyboard('Test text')); | ||
|
||
await act(async () => { | ||
await result.current.copyText(); | ||
}); | ||
|
||
expect(result.current.status).toBe(COPY_STATUS.FAILED); | ||
expect(result.current.statusText).toBe('Failed to copy link. Retry?'); | ||
}); | ||
|
||
it('should copy text to clipboard on Ctrl+C (or Cmd+C on Mac) keydown', async () => { | ||
const { result } = renderHook(() => useCopyToKeyboard('Test text')); | ||
|
||
const event = new KeyboardEvent('keydown', { key: 'c', ctrlKey: true }); | ||
document.dispatchEvent(event); | ||
|
||
await act(async () => {}); | ||
|
||
expect(navigator.clipboard.writeText as jest.Mock).toHaveBeenCalledWith('Test text'); | ||
expect(result.current.status).toBe(COPY_STATUS.COPIED); | ||
}); | ||
|
||
it('should not change status on key press other than Ctrl+C (or Cmd+C on Mac)', async () => { | ||
const { result } = renderHook(() => useCopyToKeyboard('Test text')); | ||
|
||
const event = new KeyboardEvent('keydown', { key: 'a' }); | ||
document.dispatchEvent(event); | ||
|
||
await act(async () => {}); | ||
|
||
expect(navigator.clipboard.writeText as jest.Mock).not.toHaveBeenCalled(); | ||
expect(result.current.status).toBe(COPY_STATUS.NOT_COPIED); | ||
}); | ||
}); |
Oops, something went wrong.