diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 739c88f..78de25f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -14,7 +14,8 @@ "biomejs.biome", "bradlc.vscode-tailwindcss", "davidanson.vscode-markdownlint", - "naumovs.color-highlight" + "naumovs.color-highlight", + "typescriptteam.native-preview" ] } }, diff --git a/.vscode/settings.json b/.vscode/settings.json index b955617..fdad94b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,8 @@ "source.organizeImports.biome": "explicit" }, "files.autoSave": "off", + "js/ts.experimental.useTsgo": true, + "js/ts.tsdk.path": "./node_modules/typescript", "workbench.colorTheme": "GitHub Light Default", "[markdown]": { "editor.defaultFormatter": "DavidAnson.vscode-markdownlint" diff --git a/__tests__/Speednote.test.tsx b/__tests__/Speednote.test.tsx index 81c8c10..340e9f9 100644 --- a/__tests__/Speednote.test.tsx +++ b/__tests__/Speednote.test.tsx @@ -380,18 +380,21 @@ test.each([ inputContent: "a".repeat(100), inputTitle: "Long", }, -])("able to get the share note in the proper format $constraint", async ({ - inputTitle, - inputContent, - expectedEncodedTitle, - expectedEncodedContent, -}) => { - const url = generateShareNoteUrl(inputTitle, inputContent); - - expect(url).toStrictEqual( - `${window.location.href}?title=${expectedEncodedTitle}&content=${expectedEncodedContent}`, - ); -}); +])( + "able to get the share note in the proper format $constraint", + async ({ + inputTitle, + inputContent, + expectedEncodedTitle, + expectedEncodedContent, + }) => { + const url = generateShareNoteUrl(inputTitle, inputContent); + + expect(url).toStrictEqual( + `${window.location.href}?title=${expectedEncodedTitle}&content=${expectedEncodedContent}`, + ); + }, +); // Note: I'd like to be able to test the navigation back to the `/` path, but // JSDOM doesn't support it, so it's ok. At the end, it's tested by Playwright as well, @@ -468,16 +471,15 @@ test.each([ name: "invalid content only", url: "?title=RW5jaGFudGVk&content=123", }, -])("able to handle various formats of shared note url ($name)", async ({ - url, - expectedTitle, - expectedContent, -}) => { - renderWithProviders(url); - - const { content, title } = await assertEditor(); - expect(title).toHaveValue(expectedTitle); - expect(content).toHaveValue(expectedContent); - expect(title).toHaveAttribute("readOnly"); - expect(content).toHaveAttribute("readOnly"); -}); +])( + "able to handle various formats of shared note url ($name)", + async ({ url, expectedTitle, expectedContent }) => { + renderWithProviders(url); + + const { content, title } = await assertEditor(); + expect(title).toHaveValue(expectedTitle); + expect(content).toHaveValue(expectedContent); + expect(title).toHaveAttribute("readOnly"); + expect(content).toHaveAttribute("readOnly"); + }, +); diff --git a/app/editor/external-note-action.tsx b/app/editor/external-note-action.tsx index 8e1cf94..e03133a 100644 --- a/app/editor/external-note-action.tsx +++ b/app/editor/external-note-action.tsx @@ -1,4 +1,4 @@ -import { useId, useState } from "react"; +import { type MouseEventHandler, useId, useState } from "react"; import { toast } from "sonner"; import { Button } from "~/button"; import { NoteStore } from "~/editor/store"; @@ -24,6 +24,10 @@ export const ExternalNoteAction = ({ onSave }: ExternalNoteActionProps) => { const labelId = useId(); const [generatedUrl, setGeneratedUrl] = useState(""); + const handleSelectURL: MouseEventHandler = (e) => { + e.currentTarget.select(); + }; + const handleShareNote = async () => { // Save the note initially, so that we're sure that the changes are committed. await onSave(); @@ -64,7 +68,7 @@ export const ExternalNoteAction = ({ onSave }: ExternalNoteActionProps) => { aria-labelledby={labelId} className="text-[0.5rem]" id={inputId} - onClick={({ currentTarget }) => currentTarget.select()} + onClick={handleSelectURL} readOnly type="generic" value={generatedUrl} diff --git a/app/editor/internal-note-action.tsx b/app/editor/internal-note-action.tsx index b077409..539f905 100644 --- a/app/editor/internal-note-action.tsx +++ b/app/editor/internal-note-action.tsx @@ -30,7 +30,7 @@ export const InternalNoteAction = ({ onSave }: InternalNoteActionProps) => { toast.info("Note cleared!"); }; - const handleFreezeNote = async (nextValue: boolean) => { + const handleFreezeNote = (nextValue: boolean) => async () => { setFrozen(nextValue); await onSave(); @@ -54,7 +54,7 @@ export const InternalNoteAction = ({ onSave }: InternalNoteActionProps) => { Clear content - diff --git a/app/editor/note-editor.tsx b/app/editor/note-editor.tsx index 857a464..515c38c 100644 --- a/app/editor/note-editor.tsx +++ b/app/editor/note-editor.tsx @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { type ChangeEventHandler, useEffect } from "react"; import { ExternalNoteAction } from "~/editor/external-note-action"; import { setNotes } from "~/editor/indexed-db"; import { InternalNoteAction } from "~/editor/internal-note-action"; @@ -42,11 +42,18 @@ const TitleEditor = () => { const title = useNoteStore((state) => state.title); const isFrozen = useNoteStore((state) => state.isFrozen); + const handleTitleChange: ChangeEventHandler< + HTMLTextAreaElement, + HTMLTextAreaElement + > = ({ currentTarget: { value } }) => { + setTitle(value, Date.now()); + }; + return (
setTitle(value, Date.now())} + onChange={handleTitleChange} placeholder="Enter a title" readOnly={isFrozen} type="title" @@ -60,13 +67,18 @@ const ContentEditor = () => { const content = useNoteStore((state) => state.content); const isFrozen = useNoteStore((state) => state.isFrozen); + const handleContentChange: ChangeEventHandler< + HTMLTextAreaElement, + HTMLTextAreaElement + > = ({ currentTarget: { value } }) => { + setContent(value, Date.now()); + }; + return (
- setContent(value, Date.now()) - } + onChange={handleContentChange} placeholder="Start writing. Progress saves automatically." readOnly={isFrozen} type="content" diff --git a/app/header.tsx b/app/header.tsx index b23647d..79ace18 100644 --- a/app/header.tsx +++ b/app/header.tsx @@ -12,7 +12,7 @@ export const Header = () => {