From 19ac959fcdc4aeab598e50258509e3ddd9a311bb Mon Sep 17 00:00:00 2001
From: Tyler <26290074+thegitduck@users.noreply.github.com>
Date: Thu, 24 Oct 2024 22:45:10 -0700
Subject: [PATCH 1/4] feat: Add parseAsISODate
---
packages/docs/content/docs/parsers/built-in.mdx | 13 ++++++++++++-
packages/docs/content/docs/parsers/demos.tsx | 7 ++++++-
packages/nuqs/src/parsers.test.ts | 11 +++++++++++
packages/nuqs/src/parsers.ts | 16 ++++++++++++++++
4 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/packages/docs/content/docs/parsers/built-in.mdx b/packages/docs/content/docs/parsers/built-in.mdx
index 3afb7618..c23c3c96 100644
--- a/packages/docs/content/docs/parsers/built-in.mdx
+++ b/packages/docs/content/docs/parsers/built-in.mdx
@@ -12,6 +12,7 @@ import {
BooleanParserDemo,
StringLiteralParserDemo,
DateISOParserDemo,
+ DatetimeISOParserDemo,
DateTimestampParserDemo,
JsonParserDemo
} from '@/content/docs/parsers/demos'
@@ -183,12 +184,22 @@ The query string value will be the **value** of the enum, not its name
There are two parsers that give you a `Date` object, their difference is
on how they encode the value into the query string.
-### ISO 8601
+### ISO 8601 Datetime
```ts
import { parseAsIsoDateTime } from 'nuqs'
```
+
+
+
+
+### ISO 8601 Date
+
+```ts
+import { parseAsIsoDate } from 'nuqs'
+```
+
diff --git a/packages/docs/content/docs/parsers/demos.tsx b/packages/docs/content/docs/parsers/demos.tsx
index fab14bcd..fb424271 100644
--- a/packages/docs/content/docs/parsers/demos.tsx
+++ b/packages/docs/content/docs/parsers/demos.tsx
@@ -14,6 +14,7 @@ import {
parseAsFloat,
parseAsHex,
parseAsInteger,
+ parseAsIsoDate,
parseAsIsoDateTime,
parseAsJson,
parseAsStringLiteral,
@@ -290,10 +291,14 @@ export function DateParserDemo({
)
}
-export function DateISOParserDemo() {
+export function DatetimeISOParserDemo() {
return
}
+export function DateISOParserDemo() {
+ return
+}
+
export function DateTimestampParserDemo() {
return
}
diff --git a/packages/nuqs/src/parsers.test.ts b/packages/nuqs/src/parsers.test.ts
index e2fb37ce..027c6bf1 100644
--- a/packages/nuqs/src/parsers.test.ts
+++ b/packages/nuqs/src/parsers.test.ts
@@ -5,6 +5,7 @@ import {
parseAsHex,
parseAsInteger,
parseAsIsoDateTime,
+ parseAsIsoDate,
parseAsString,
parseAsTimestamp
} from './parsers'
@@ -48,6 +49,16 @@ describe('parsers', () => {
ref
)
})
+ test('parseAsIsoDate', () => {
+ expect(parseAsIsoDate.parse('')).toBeNull()
+ expect(parseAsIsoDate.parse('not-a-date')).toBeNull()
+ const moment = '2020-01-01'
+ const ref = new Date(moment)
+ expect(parseAsIsoDate.parse(moment)).toStrictEqual(ref)
+ expect(parseAsIsoDate.parse(moment.slice(0, 10))).toStrictEqual(ref)
+ expect(parseAsIsoDate.serialize(ref).length).toBe(10)
+ expect(parseAsIsoDate.serialize(ref).length).not.toContain('T')
+ })
test('parseAsArrayOf', () => {
const parser = parseAsArrayOf(parseAsString)
expect(parser.serialize([])).toBe('')
diff --git a/packages/nuqs/src/parsers.ts b/packages/nuqs/src/parsers.ts
index 22d45cc1..ecaf7a40 100644
--- a/packages/nuqs/src/parsers.ts
+++ b/packages/nuqs/src/parsers.ts
@@ -215,6 +215,22 @@ export const parseAsIsoDateTime = createParser({
serialize: (v: Date) => v.toISOString()
})
+/**
+ * Querystring encoded as an ISO-8601 string (UTC)
+ * without the time zone offset, and returned as
+ * a Date object.
+ */
+export const parseAsIsoDate = createParser({
+ parse: v => {
+ const date = new Date(v + 'T00:00:00.000Z')
+ if (Number.isNaN(date.valueOf())) {
+ return null
+ }
+ return date
+ },
+ serialize: (v: Date) => v.toISOString().slice(0, 10)
+})
+
/**
* String-based enums provide better type-safety for known sets of values.
* You will need to pass the parseAsStringEnum function a list of your enum values
From 8e964f5304127a789793d63790ec0ed3e45556c6 Mon Sep 17 00:00:00 2001
From: Tyler
Date: Fri, 25 Oct 2024 15:10:13 -0700
Subject: [PATCH 2/4] fix: Comments from review
---
.../docs/content/docs/parsers/built-in.mdx | 56 +++++++++----------
packages/docs/content/docs/parsers/demos.tsx | 26 +++++++--
packages/nuqs/src/parsers.test.ts | 4 +-
packages/nuqs/src/parsers.ts | 5 +-
4 files changed, 54 insertions(+), 37 deletions(-)
diff --git a/packages/docs/content/docs/parsers/built-in.mdx b/packages/docs/content/docs/parsers/built-in.mdx
index c23c3c96..9dda246b 100644
--- a/packages/docs/content/docs/parsers/built-in.mdx
+++ b/packages/docs/content/docs/parsers/built-in.mdx
@@ -30,8 +30,8 @@ This is where **parsers** come in.
import { parseAsString } from 'nuqs'
```
-}>
-
+}>
+
@@ -40,6 +40,7 @@ and will accept **any** value.
If you're expecting a certain set of string values, like `'foo' | 'bar'{:ts}`,
see [Literals](#literals) for ensuring type-runtime safety.
+
If search params are strings by default, what's the point of this _"parser"_ ?
@@ -68,11 +69,10 @@ import { parseAsInteger } from 'nuqs'
useQueryState('int', parseAsInteger.withDefault(0))
```
-}>
-
+}>
+
-
### Floating point
Same as integer, but uses `parseFloat` under the hood.
@@ -83,11 +83,10 @@ import { parseAsFloat } from 'nuqs'
useQueryState('float', parseAsFloat.withDefault(0))
```
-}>
-
+}>
+
-
### Hexadecimal
Encodes integers in hexadecimal.
@@ -98,12 +97,12 @@ import { parseAsHex } from 'nuqs'
useQueryState('hex', parseAsHex.withDefault(0x00))
```
-}>
-
+}>
+
- Check out the [Hex Colors](/playground/hex-colors) playground for a demo.
+ Check out the [Hex Colors](/playground/hex-colors) playground for a demo.
## Boolean
@@ -114,8 +113,8 @@ import { parseAsBoolean } from 'nuqs'
useQueryState('bool', parseAsBoolean.withDefault(false))
```
-}>
-
+}>
+
## Literals
@@ -139,14 +138,13 @@ const sortOrder = ['asc', 'desc'] as const
parseAsStringLiteral(sortOrder)
// Optional: extract the type from them
-type SortOrder = (typeof sortOrder)[number]; // 'asc' | 'desc'
+type SortOrder = (typeof sortOrder)[number] // 'asc' | 'desc'
```
-}>
-
+}>
+
-
### Numeric literals
```ts /as const/
@@ -175,8 +173,8 @@ parseAsStringEnum(Object.values(Direction))
```
-The query string value will be the **value** of the enum, not its name
-(here: `?direction=UP`).
+ The query string value will be the **value** of the enum, not its name (here:
+ `?direction=UP`).
## Dates & timestamps
@@ -191,17 +189,19 @@ import { parseAsIsoDateTime } from 'nuqs'
```
-
+
### ISO 8601 Date
+Note: the Date is parsed without the time zone offset, making it at GMT 00:00:00 UTC.
+
```ts
import { parseAsIsoDate } from 'nuqs'
```
-
+
### Timestamp
@@ -213,7 +213,7 @@ import { parseAsTimestamp } from 'nuqs'
```
-
+
## Arrays
@@ -252,14 +252,14 @@ const schema = z.object({
const [json, setJson] = useQueryState('json', parseAsJson(schema.parse))
setJson({
- pkg: "nuqs",
+ pkg: 'nuqs',
version: 2,
- worksWith: ["Next.js", "React", "Remix", "React Router", "and more"],
-});
+ worksWith: ['Next.js', 'React', 'Remix', 'React Router', 'and more']
+})
```
-
+
Using other validation libraries is possible, as long as they throw an error
@@ -275,6 +275,6 @@ import { parseAsString } from 'nuqs/server'
```
-It used to be available under the alias import `nuqs/parsers`,
-which will be dropped in the next major version.
+ It used to be available under the alias import `nuqs/parsers`, which will be
+ dropped in the next major version.
diff --git a/packages/docs/content/docs/parsers/demos.tsx b/packages/docs/content/docs/parsers/demos.tsx
index fb424271..4f2132a7 100644
--- a/packages/docs/content/docs/parsers/demos.tsx
+++ b/packages/docs/content/docs/parsers/demos.tsx
@@ -247,10 +247,12 @@ export function StringLiteralParserDemo() {
export function DateParserDemo({
queryKey,
- parser
+ parser,
+ type
}: {
queryKey: string
parser: ParserBuilder
+ type: 'date' | 'datetime-local'
}) {
const [value, setValue] = useQueryState(queryKey, parser)
return (
@@ -258,7 +260,7 @@ export function DateParserDemo({
{
@@ -292,15 +294,29 @@ export function DateParserDemo({
}
export function DatetimeISOParserDemo() {
- return
+ return (
+
+ )
}
export function DateISOParserDemo() {
- return
+ return (
+
+ )
}
export function DateTimestampParserDemo() {
- return
+ return (
+
+ )
}
const jsonParserSchema = z.object({
diff --git a/packages/nuqs/src/parsers.test.ts b/packages/nuqs/src/parsers.test.ts
index 027c6bf1..74d9d3d0 100644
--- a/packages/nuqs/src/parsers.test.ts
+++ b/packages/nuqs/src/parsers.test.ts
@@ -55,9 +55,7 @@ describe('parsers', () => {
const moment = '2020-01-01'
const ref = new Date(moment)
expect(parseAsIsoDate.parse(moment)).toStrictEqual(ref)
- expect(parseAsIsoDate.parse(moment.slice(0, 10))).toStrictEqual(ref)
- expect(parseAsIsoDate.serialize(ref).length).toBe(10)
- expect(parseAsIsoDate.serialize(ref).length).not.toContain('T')
+ expect(parseAsIsoDate.serialize(ref)).toEqual(moment)
})
test('parseAsArrayOf', () => {
const parser = parseAsArrayOf(parseAsString)
diff --git a/packages/nuqs/src/parsers.ts b/packages/nuqs/src/parsers.ts
index ecaf7a40..735687fb 100644
--- a/packages/nuqs/src/parsers.ts
+++ b/packages/nuqs/src/parsers.ts
@@ -219,10 +219,13 @@ export const parseAsIsoDateTime = createParser({
* Querystring encoded as an ISO-8601 string (UTC)
* without the time zone offset, and returned as
* a Date object.
+ *
+ * The Date is parsed without the time zone offset,
+ * making it at GMT 00:00:00 UTC.
*/
export const parseAsIsoDate = createParser({
parse: v => {
- const date = new Date(v + 'T00:00:00.000Z')
+ const date = new Date(v.slice(0, 10))
if (Number.isNaN(date.valueOf())) {
return null
}
From 9cc989b2a271496a1f6618d1414bfb5c60607900 Mon Sep 17 00:00:00 2001
From: Tyler <26290074+tylersayshi@users.noreply.github.com>
Date: Fri, 25 Oct 2024 16:07:00 -0700
Subject: [PATCH 3/4] fix: Remove GMT
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: François Best
---
packages/nuqs/src/parsers.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/nuqs/src/parsers.ts b/packages/nuqs/src/parsers.ts
index 735687fb..82973a60 100644
--- a/packages/nuqs/src/parsers.ts
+++ b/packages/nuqs/src/parsers.ts
@@ -221,7 +221,7 @@ export const parseAsIsoDateTime = createParser({
* a Date object.
*
* The Date is parsed without the time zone offset,
- * making it at GMT 00:00:00 UTC.
+ * making it at 00:00:00 UTC.
*/
export const parseAsIsoDate = createParser({
parse: v => {
From f8b2697f993a8b8a2865fa831e82e183f72534db Mon Sep 17 00:00:00 2001
From: Tyler
Date: Fri, 25 Oct 2024 16:08:14 -0700
Subject: [PATCH 4/4] fix: Query keys
---
packages/docs/content/docs/parsers/demos.tsx | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/packages/docs/content/docs/parsers/demos.tsx b/packages/docs/content/docs/parsers/demos.tsx
index 4f2132a7..2429926d 100644
--- a/packages/docs/content/docs/parsers/demos.tsx
+++ b/packages/docs/content/docs/parsers/demos.tsx
@@ -297,16 +297,14 @@ export function DatetimeISOParserDemo() {
return (
)
}
export function DateISOParserDemo() {
- return (
-
- )
+ return
}
export function DateTimestampParserDemo() {