Skip to content

Commit 887dcb7

Browse files
committed
feat(extension): add notes page
1 parent afd4c9c commit 887dcb7

30 files changed

+402
-54
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
import { useState } from 'react'
2+
import { ExtensionError } from '../action-error'
13
import { ExtensionHeader } from '../action-header'
4+
import { Notes } from '../notes'
25
import { SavedScreen } from '../saved'
6+
import { SavedLoader } from '../saved-loader'
37

48
// Types
5-
import { ExtensionFooter } from '../action-footer'
69
import type { ExtPreview } from '../../types'
710
import type { NoteEdge } from '@common/types/pocket'
811

912
export function ActionContainer({
1013
isOpen = false,
1114
errorMessage,
1215
preview,
13-
saveStatus,
1416
notes,
1517
tags
1618
}: {
@@ -22,15 +24,28 @@ export function ActionContainer({
2224
tags?: string[]
2325
actionUnSave: () => void
2426
}) {
27+
const [showNotes, setShowNotes] = useState<boolean>(false)
28+
2529
return isOpen ? (
2630
<div className="extension">
27-
<ExtensionHeader saveStatus={saveStatus} />
31+
<ExtensionHeader />
2832
{preview ? (
2933
<>
30-
<SavedScreen preview={preview} notes={notes} tags={tags} />
31-
<ExtensionFooter errorMessage={errorMessage} />
34+
{showNotes ? (
35+
<Notes notes={notes} setShowNotes={setShowNotes} />
36+
) : (
37+
<SavedScreen
38+
preview={preview}
39+
tags={tags}
40+
errorMessage={errorMessage}
41+
setShowNotes={setShowNotes}
42+
/>
43+
)}
3244
</>
33-
) : null}
45+
) : (
46+
<SavedLoader />
47+
)}
48+
{errorMessage ? <ExtensionError errorMessage={errorMessage} /> : null}
3449
</div>
3550
) : null
3651
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Components
2+
import { ExtensionError as Component } from '.'
3+
4+
// Types
5+
import type { Meta, StoryObj } from '@storybook/react'
6+
7+
// Storybook Meta
8+
const meta: Meta<typeof Component> = {
9+
title: 'Action - Error',
10+
component: Component
11+
}
12+
export default meta
13+
14+
// Stories
15+
export const ActionError: StoryObj<typeof Component> = {
16+
render: (args) => {
17+
return <Component errorMessage={args.errorMessage} />
18+
},
19+
args: {
20+
errorMessage: 'Oops, something is awry.'
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import style from './style.module.css'
2+
3+
export function ExtensionError({ errorMessage }: { errorMessage?: string }) {
4+
return <div className={style.error}> {errorMessage} </div>
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.error {
2+
border-top: 1px solid var(--color-divider);
3+
padding: 1rem;
4+
font-size: 0.85rem;
5+
font-style: italic;
6+
color: var(--color-error);
7+
}

clients/extension/components/action-header/index.tsx

+1-4
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@ import { OverflowMenuIcon } from '../icons/OverflowMenuIcon'
33
import { PocketLogo } from '../icons/Pocket'
44
// import { SaveFilledIcon } from '../icons/SaveFilledIcon'
55
// import { SaveIcon } from '../icons/SaveIcon'
6-
import { SavedLoader } from '../saved-loader'
76

8-
export function ExtensionHeader({ saveStatus }: { saveStatus?: string }) {
9-
const loaded = saveStatus !== 'saving'
7+
export function ExtensionHeader() {
108
return (
119
<header className={style.header}>
1210
{/* {loaded ? <SaveFilledIcon className={style.icon} /> : <SaveIcon className={style.icon} />} */}
1311
<PocketLogo />
14-
{loaded ? <div /> : <SavedLoader />}
1512
<OverflowMenuIcon />
1613
</header>
1714
)

clients/extension/components/action-header/style.module.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.header {
22
display: grid;
3-
grid-template-columns: 32px auto 32px;
3+
grid-template-columns: auto 32px;
44
align-items: center;
55
padding: 0.5rem 1rem;
66
font-size: 1rem;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { SVGProps } from 'react'
2+
3+
export const AddNoteIcon = ({ className, ...rest }: SVGProps<SVGSVGElement>) => {
4+
const iconClass = ['icon', className && className].join(' ')
5+
return (
6+
<svg
7+
className={iconClass}
8+
{...rest}
9+
width="24"
10+
height="24"
11+
viewBox="0 0 24 24"
12+
fill="none"
13+
xmlns="http://www.w3.org/2000/svg">
14+
<path
15+
d="M4 5H20V19.6522C20 19.9609 19.8601 20.2907 19.553 20.5577C19.2415 20.8286 18.7925 21 18.3 21H5.7C5.2075 21 4.75852 20.8286 4.44699 20.5577C4.13989 20.2907 4 19.9609 4 19.6522V5Z"
16+
stroke="currentColor"
17+
strokeWidth="2"
18+
strokeLinecap="round"
19+
strokeLinejoin="round"
20+
/>
21+
<path
22+
d="M8 3V4"
23+
stroke="currentColor"
24+
strokeWidth="2"
25+
strokeLinecap="round"
26+
strokeLinejoin="round"
27+
/>
28+
<path
29+
d="M12 3V4"
30+
stroke="currentColor"
31+
strokeWidth="2"
32+
strokeLinecap="round"
33+
strokeLinejoin="round"
34+
/>
35+
<path
36+
d="M16 3V4"
37+
stroke="currentColor"
38+
strokeWidth="2"
39+
strokeLinecap="round"
40+
strokeLinejoin="round"
41+
/>
42+
<path
43+
fillRule="evenodd"
44+
clipRule="evenodd"
45+
d="M12.0001 17.5C11.4478 17.5 11.0001 17.0523 11.0001 16.5V8.5C11.0001 7.94772 11.4478 7.5 12.0001 7.5C12.5523 7.5 13.0001 7.94772 13.0001 8.5V16.5C13.0001 17.0523 12.5523 17.5 12.0001 17.5Z"
46+
fill="currentColor"
47+
/>
48+
<path
49+
fillRule="evenodd"
50+
clipRule="evenodd"
51+
d="M7 12.4998C7 11.9475 7.44772 11.4998 8 11.4998H16C16.5523 11.4998 17 11.9475 17 12.4998C17 13.052 16.5523 13.4998 16 13.4998H8C7.44772 13.4998 7 13.052 7 12.4998Z"
52+
fill="currentColor"
53+
/>
54+
</svg>
55+
)
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { SVGProps } from 'react'
2+
3+
export const ArrowLeftIcon = ({ className, ...rest }: SVGProps<SVGSVGElement>) => {
4+
const iconClass = ['icon', className && className].join(' ')
5+
return (
6+
<svg
7+
fill="currentColor"
8+
xmlns="http://www.w3.org/2000/svg"
9+
viewBox="0 0 24 24"
10+
aria-hidden="true"
11+
className={iconClass}
12+
{...rest}>
13+
<path d="M12.707 5.707a1 1 0 0 0-1.414-1.414l-7 7a1 1 0 0 0 0 1.414l7 7a1 1 0 0 0 1.414-1.414L7.414 13H19.05c.525 0 .95-.448.95-1s-.425-1-.95-1H7.414z" />
14+
</svg>
15+
)
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { SVGProps } from 'react'
2+
3+
export const ChevronLeftIcon = ({ className, ...rest }: SVGProps<SVGSVGElement>) => {
4+
const iconClass = ['icon', className && className].join(' ')
5+
return (
6+
<svg
7+
fill="currentColor"
8+
xmlns="http://www.w3.org/2000/svg"
9+
viewBox="0 0 24 24"
10+
aria-hidden="true"
11+
className={iconClass}
12+
{...rest}>
13+
<path
14+
fillRule="evenodd"
15+
d="M14.707 4.293a1 1 0 0 1 0 1.414L8.414 12l6.293 6.293a1 1 0 0 1-1.414 1.414l-7-7a1 1 0 0 1 0-1.414l7-7a1 1 0 0 1 1.414 0"
16+
clipRule="evenodd"
17+
/>
18+
</svg>
19+
)
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Components
2+
import { useRef } from 'react'
3+
import { NotesAdd as Component } from '.'
4+
5+
// Types
6+
import type { Meta, StoryObj } from '@storybook/react'
7+
8+
// Storybook Meta
9+
const meta: Meta<typeof Component> = {
10+
title: 'Notes - Add',
11+
component: Component
12+
}
13+
export default meta
14+
15+
// Stories
16+
export const NotesAdd: StoryObj<typeof Component> = {
17+
render: () => {
18+
const textRef = useRef<HTMLTextAreaElement | null>(null)
19+
return <Component textRef={textRef} />
20+
},
21+
args: {}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import style from './style.module.css'
2+
3+
import type { RefObject } from 'react'
4+
5+
export function NotesAdd({ textRef }: { textRef?: RefObject<HTMLTextAreaElement | null> }) {
6+
return (
7+
<div className={style.container}>
8+
<textarea name="note" id="note" ref={textRef}></textarea>
9+
</div>
10+
)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.container {
2+
padding: 0.5rem 1rem 1rem;
3+
textarea {
4+
margin: 0 0 0.5rem 0;
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Components
2+
import { NotesFooter as Component } from '.'
3+
4+
// Types
5+
import type { Meta, StoryObj } from '@storybook/react'
6+
7+
// Storybook Meta
8+
const meta: Meta<typeof Component> = {
9+
title: 'Notes - Footer',
10+
component: Component
11+
}
12+
export default meta
13+
14+
// Stories
15+
export const NotesFooter: StoryObj<typeof Component> = {
16+
render: () => {
17+
const setShowNotes = () => {}
18+
return (
19+
<Component
20+
setShowNotes={setShowNotes}
21+
handleAddNote={function (): void {
22+
throw new Error('Function not implemented.')
23+
}}
24+
/>
25+
)
26+
},
27+
args: {}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import style from './style.module.css'
2+
import { AddNoteIcon } from '../icons/AddNoteIcon'
3+
import { ChevronLeftIcon } from '../icons/ChevronLeftIcon'
4+
5+
// Types
6+
import type { Dispatch, SetStateAction } from 'react'
7+
8+
export function NotesFooter({
9+
setShowNotes,
10+
handleAddNote = () => {}
11+
}: {
12+
setShowNotes: Dispatch<SetStateAction<boolean>>
13+
handleAddNote: () => void
14+
}) {
15+
const handleBackClick = () => setShowNotes(false)
16+
return (
17+
<footer className={style.footer}>
18+
<button onClick={handleBackClick}>
19+
<ChevronLeftIcon className={style.backIcon} /> Back
20+
</button>
21+
<button onClick={handleAddNote}>
22+
<AddNoteIcon /> Add Note
23+
</button>
24+
</footer>
25+
)
26+
}

clients/extension/components/action-footer/style.module.css clients/extension/components/notes-footer/style.module.css

+2-4
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
letter-spacing: -0.03em;
1111
}
1212

13-
.error {
14-
font-size: 0.85rem;
15-
font-style: italic;
16-
color: var(--color-error);
13+
.backIcon {
14+
margin-right: 0;
1715
}

clients/extension/components/action-footer/component.story.tsx clients/extension/components/notes-list/component.story.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
// Components
2-
import { ExtensionFooter as Component } from '.'
2+
import { NotesList as Component } from '.'
33

44
// Types
55
import type { Meta, StoryObj } from '@storybook/react'
66

77
// Storybook Meta
88
const meta: Meta<typeof Component> = {
9-
title: 'Action - Footer',
9+
title: 'Notes - List',
1010
component: Component
1111
}
1212
export default meta
1313

1414
// Stories
15-
export const ActionFooter: StoryObj<typeof Component> = {
15+
export const NotesList: StoryObj<typeof Component> = {
1616
render: () => {
1717
return <Component />
1818
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import style from './style.module.css'
2+
3+
// Types
4+
import type { Note, NoteEdge } from '@common/types/pocket'
5+
6+
export function NotesList({ notes }: { notes?: NoteEdge[] }) {
7+
return (
8+
<div className={style.notes}>
9+
{Array.isArray(notes) && notes.length ? (
10+
notes.map((note) => <Note key={note?.cursor} note={note} />)
11+
) : (
12+
<NoNotes />
13+
)}
14+
</div>
15+
)
16+
}
17+
18+
function Note({ note }: { note?: NoteEdge }) {
19+
const node = note?.node
20+
const contentPreview = node?.contentPreview as string
21+
return contentPreview ? <div>{contentPreview}</div> : null
22+
}
23+
24+
function NoNotes() {
25+
return <div className={style.noNotes}>Add your first note to this page</div>
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.notes {
2+
padding: 1rem;
3+
}
4+
5+
.noNotes {
6+
font-size: 0.825rem;
7+
font-style: italic;
8+
color: var(--color-text-secondary);
9+
}

0 commit comments

Comments
 (0)