lightnet 3.10.5 → 3.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/__e2e__/admin.spec.ts +29 -48
- package/__e2e__/{test-utils.ts → basics-fixture.ts} +20 -23
- package/__e2e__/fixtures/basics/node_modules/.bin/astro +2 -2
- package/__e2e__/fixtures/basics/package.json +2 -2
- package/__e2e__/fixtures/basics/src/content/media/faithful-freestyle--en.json +1 -1
- package/__e2e__/fixtures/basics/src/content/media/skate-sounds--en.json +1 -1
- package/__e2e__/global.teardown.ts +5 -0
- package/__e2e__/homepage.spec.ts +17 -19
- package/__e2e__/search.spec.ts +3 -5
- package/package.json +4 -3
- package/playwright.config.ts +1 -0
- package/src/admin/components/form/Input.tsx +3 -0
- package/src/admin/components/form/LazyLoadedMarkdownEditor.tsx +78 -0
- package/src/admin/components/form/MarkdownEditor.tsx +52 -0
- package/src/admin/components/form/Select.tsx +3 -0
- package/src/admin/components/form/atoms/Label.tsx +1 -1
- package/src/admin/components/form/atoms/Legend.tsx +1 -1
- package/src/admin/i18n/translations/en.yml +1 -0
- package/src/admin/pages/media/EditForm.tsx +30 -4
- package/src/admin/pages/media/EditRoute.astro +2 -2
- package/src/admin/pages/media/fields/Authors.tsx +17 -2
- package/src/admin/pages/media/fields/Categories.tsx +6 -0
- package/src/admin/pages/media/fields/Collections.tsx +15 -1
- package/src/admin/types/media-item.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# lightnet
|
|
2
2
|
|
|
3
|
+
## 3.10.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#333](https://github.com/LightNetDev/LightNet/pull/333) [`539a377`](https://github.com/LightNetDev/LightNet/commit/539a377702df9213d0869ae63646f569c10b1867) Thanks [@smn-cds](https://github.com/smn-cds)! - Update dependencies
|
|
8
|
+
|
|
3
9
|
## 3.10.5
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/__e2e__/admin.spec.ts
CHANGED
|
@@ -2,9 +2,8 @@ import { readFile } from "node:fs/promises"
|
|
|
2
2
|
|
|
3
3
|
import { expect, type Page } from "@playwright/test"
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { test } from "./basics-fixture"
|
|
6
6
|
|
|
7
|
-
const test = lightnetTest("./fixtures/basics/")
|
|
8
7
|
const faithfulFreestyleMediaUrl = new URL(
|
|
9
8
|
"./fixtures/basics/src/content/media/faithful-freestyle--en.json",
|
|
10
9
|
import.meta.url,
|
|
@@ -13,9 +12,9 @@ const faithfulFreestyleMediaUrl = new URL(
|
|
|
13
12
|
test.describe("Edit button on details page", () => {
|
|
14
13
|
test("Should not show `Edit` button on details page by default.", async ({
|
|
15
14
|
page,
|
|
16
|
-
|
|
15
|
+
lightnet,
|
|
17
16
|
}) => {
|
|
18
|
-
await
|
|
17
|
+
await lightnet()
|
|
19
18
|
|
|
20
19
|
await page.getByRole("link", { name: "Faithful Freestyle" }).click()
|
|
21
20
|
await expect(
|
|
@@ -28,9 +27,9 @@ test.describe("Edit button on details page", () => {
|
|
|
28
27
|
|
|
29
28
|
test("Should show `Edit` button on book details page after visiting `/en/admin/` path.", async ({
|
|
30
29
|
page,
|
|
31
|
-
|
|
30
|
+
lightnet,
|
|
32
31
|
}) => {
|
|
33
|
-
const ln = await
|
|
32
|
+
const ln = await lightnet()
|
|
34
33
|
|
|
35
34
|
await page.goto(ln.resolveURL("/en/admin/"))
|
|
36
35
|
await expect(
|
|
@@ -52,11 +51,10 @@ test.describe("Edit button on details page", () => {
|
|
|
52
51
|
|
|
53
52
|
test("Should show `Edit` button on video details page after visiting `/en/admin/` path.", async ({
|
|
54
53
|
page,
|
|
55
|
-
|
|
54
|
+
lightnet,
|
|
56
55
|
}) => {
|
|
57
|
-
const ln = await
|
|
56
|
+
const ln = await lightnet("/en/admin/")
|
|
58
57
|
|
|
59
|
-
await page.goto(ln.resolveURL("/en/admin/"))
|
|
60
58
|
await expect(
|
|
61
59
|
page.getByText("Admin features are enabled now.", { exact: true }),
|
|
62
60
|
).toBeVisible()
|
|
@@ -76,11 +74,10 @@ test.describe("Edit button on details page", () => {
|
|
|
76
74
|
|
|
77
75
|
test("Should show `Edit` button on audio details page after visiting `/en/admin/` path.", async ({
|
|
78
76
|
page,
|
|
79
|
-
|
|
77
|
+
lightnet,
|
|
80
78
|
}) => {
|
|
81
|
-
const ln = await
|
|
79
|
+
const ln = await lightnet("/en/admin/")
|
|
82
80
|
|
|
83
|
-
await page.goto(ln.resolveURL("/en/admin/"))
|
|
84
81
|
await expect(
|
|
85
82
|
page.getByText("Admin features are enabled now.", { exact: true }),
|
|
86
83
|
).toBeVisible()
|
|
@@ -100,12 +97,10 @@ test.describe("Edit button on details page", () => {
|
|
|
100
97
|
|
|
101
98
|
test("Edit button on details page should navigate to media item edit page", async ({
|
|
102
99
|
page,
|
|
103
|
-
|
|
100
|
+
lightnet,
|
|
104
101
|
}) => {
|
|
105
|
-
const ln = await
|
|
106
|
-
|
|
107
|
-
await page.goto(ln.resolveURL("/en/admin/"))
|
|
108
|
-
await page.goto(ln.resolveURL("/en/media/faithful-freestyle--en"))
|
|
102
|
+
const ln = await lightnet("/en/admin/")
|
|
103
|
+
await ln.goto("/en/media/faithful-freestyle--en")
|
|
109
104
|
|
|
110
105
|
const editButton = page.locator("#edit-btn")
|
|
111
106
|
await expect(editButton).toBeVisible()
|
|
@@ -151,13 +146,10 @@ test.describe("Media item edit page", () => {
|
|
|
151
146
|
page.getByRole("button", { name: "Published" }).first(),
|
|
152
147
|
).toBeVisible()
|
|
153
148
|
|
|
154
|
-
test("should edit title", async ({ page,
|
|
155
|
-
|
|
156
|
-
|
|
149
|
+
test("should edit title", async ({ page, lightnet }) => {
|
|
150
|
+
await lightnet("/en/admin/media/faithful-freestyle--en")
|
|
157
151
|
const writeFileRequest = await recordWriteFile(page)
|
|
158
152
|
|
|
159
|
-
await page.goto(ln.resolveURL("/en/admin/media/faithful-freestyle--en"))
|
|
160
|
-
|
|
161
153
|
const updatedTitle = "Faithful Freestyle (Edited)"
|
|
162
154
|
const titleInput = page.getByLabel("Title")
|
|
163
155
|
await expect(titleInput).toHaveValue("Faithful Freestyle")
|
|
@@ -182,13 +174,11 @@ test.describe("Media item edit page", () => {
|
|
|
182
174
|
await expectPublishedMessage(page)
|
|
183
175
|
})
|
|
184
176
|
|
|
185
|
-
test("Should update media type", async ({ page,
|
|
186
|
-
|
|
177
|
+
test("Should update media type", async ({ page, lightnet }) => {
|
|
178
|
+
await lightnet("/en/admin/media/faithful-freestyle--en")
|
|
187
179
|
const writeFileRequest = await recordWriteFile(page)
|
|
188
180
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const typeSelect = page.getByLabel("Type")
|
|
181
|
+
const typeSelect = page.getByLabel("Type").first()
|
|
192
182
|
await expect(typeSelect).toHaveValue("book")
|
|
193
183
|
await typeSelect.selectOption("video")
|
|
194
184
|
|
|
@@ -207,12 +197,10 @@ test.describe("Media item edit page", () => {
|
|
|
207
197
|
await expectPublishedMessage(page)
|
|
208
198
|
})
|
|
209
199
|
|
|
210
|
-
test("Should update author name", async ({ page,
|
|
211
|
-
|
|
200
|
+
test("Should update author name", async ({ page, lightnet }) => {
|
|
201
|
+
await lightnet("/en/admin/media/faithful-freestyle--en")
|
|
212
202
|
const writeFileRequest = await recordWriteFile(page)
|
|
213
203
|
|
|
214
|
-
await page.goto(ln.resolveURL("/en/admin/media/faithful-freestyle--en"))
|
|
215
|
-
|
|
216
204
|
const authorsFieldset = page.getByRole("group", { name: "Authors" })
|
|
217
205
|
const firstAuthorInput = authorsFieldset.getByRole("textbox").first()
|
|
218
206
|
const updatedAuthor = "Sk8 Ministries International"
|
|
@@ -234,12 +222,10 @@ test.describe("Media item edit page", () => {
|
|
|
234
222
|
await expectPublishedMessage(page)
|
|
235
223
|
})
|
|
236
224
|
|
|
237
|
-
test("Should add author", async ({ page,
|
|
238
|
-
|
|
225
|
+
test("Should add author", async ({ page, lightnet }) => {
|
|
226
|
+
await lightnet("/en/admin/media/faithful-freestyle--en")
|
|
239
227
|
const writeFileRequest = await recordWriteFile(page)
|
|
240
228
|
|
|
241
|
-
await page.goto(ln.resolveURL("/en/admin/media/faithful-freestyle--en"))
|
|
242
|
-
|
|
243
229
|
const authorsFieldset = page.getByRole("group", { name: "Authors" })
|
|
244
230
|
const addAuthorButton = page.getByRole("button", { name: "Add Author" })
|
|
245
231
|
await addAuthorButton.click()
|
|
@@ -262,12 +248,10 @@ test.describe("Media item edit page", () => {
|
|
|
262
248
|
await expectPublishedMessage(page)
|
|
263
249
|
})
|
|
264
250
|
|
|
265
|
-
test("Should remove author", async ({ page,
|
|
266
|
-
|
|
251
|
+
test("Should remove author", async ({ page, lightnet }) => {
|
|
252
|
+
await lightnet("/en/admin/media/faithful-freestyle--en")
|
|
267
253
|
const writeFileRequest = await recordWriteFile(page)
|
|
268
254
|
|
|
269
|
-
await page.goto(ln.resolveURL("/en/admin/media/faithful-freestyle--en"))
|
|
270
|
-
|
|
271
255
|
const authorsFieldset = page.getByRole("group", { name: "Authors" })
|
|
272
256
|
const addAuthorButton = page.getByRole("button", { name: "Add Author" })
|
|
273
257
|
const replacementAuthor = "Skate Evangelists"
|
|
@@ -297,10 +281,9 @@ test.describe("Media item edit page", () => {
|
|
|
297
281
|
|
|
298
282
|
test("should show error message if common id is set empty", async ({
|
|
299
283
|
page,
|
|
300
|
-
|
|
284
|
+
lightnet,
|
|
301
285
|
}) => {
|
|
302
|
-
|
|
303
|
-
await page.goto(ln.resolveURL("/en/admin/media/faithful-freestyle--en"))
|
|
286
|
+
await lightnet("/en/admin/media/faithful-freestyle--en")
|
|
304
287
|
|
|
305
288
|
const commonIdInput = page.getByLabel("Common ID")
|
|
306
289
|
await expect(commonIdInput).toHaveValue("faithful-freestyle")
|
|
@@ -317,10 +300,9 @@ test.describe("Media item edit page", () => {
|
|
|
317
300
|
|
|
318
301
|
test("should focus invalid field when submitting invalid form data", async ({
|
|
319
302
|
page,
|
|
320
|
-
|
|
303
|
+
lightnet,
|
|
321
304
|
}) => {
|
|
322
|
-
|
|
323
|
-
await page.goto(ln.resolveURL("/en/admin/media/faithful-freestyle--en"))
|
|
305
|
+
await lightnet("/en/admin/media/faithful-freestyle--en")
|
|
324
306
|
|
|
325
307
|
const categoriesFieldset = page.getByRole("group", { name: "Categories" })
|
|
326
308
|
await page.getByRole("button", { name: "Add Category" }).click()
|
|
@@ -341,10 +323,9 @@ test.describe("Media item edit page", () => {
|
|
|
341
323
|
|
|
342
324
|
test("should not allow assigning duplicate categories", async ({
|
|
343
325
|
page,
|
|
344
|
-
|
|
326
|
+
lightnet,
|
|
345
327
|
}) => {
|
|
346
|
-
|
|
347
|
-
await page.goto(ln.resolveURL("/en/admin/media/faithful-freestyle--en"))
|
|
328
|
+
await lightnet("/en/admin/media/faithful-freestyle--en")
|
|
348
329
|
|
|
349
330
|
const categoriesFieldset = page.getByRole("group", { name: "Categories" })
|
|
350
331
|
await page.getByRole("button", { name: "Add Category" }).click()
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
25
|
* SOFTWARE.
|
|
26
26
|
*/
|
|
27
|
-
|
|
28
27
|
import { fileURLToPath } from "node:url"
|
|
29
28
|
|
|
30
29
|
import { type Page, test as baseTest } from "@playwright/test"
|
|
@@ -34,30 +33,26 @@ export { expect, type Locator } from "@playwright/test"
|
|
|
34
33
|
|
|
35
34
|
process.env.ASTRO_TELEMETRY_DISABLED = "true"
|
|
36
35
|
process.env.ASTRO_DISABLE_UPDATE_CHECK = "true"
|
|
37
|
-
|
|
38
|
-
const root = fileURLToPath(new URL(fixturePath, import.meta.url))
|
|
39
|
-
|
|
40
|
-
let server: Server | null = null
|
|
41
|
-
const test = baseTest.extend<{
|
|
42
|
-
startLightnet: (path?: string) => Promise<LightNetPage>
|
|
43
|
-
}>({
|
|
44
|
-
startLightnet: ({ page }, use) =>
|
|
45
|
-
use(async (path) => {
|
|
46
|
-
if (!server) {
|
|
47
|
-
await build({ logLevel: "error", root })
|
|
48
|
-
server = await preview({ logLevel: "error", root })
|
|
49
|
-
}
|
|
50
|
-
const ln = new LightNetPage(server, page)
|
|
51
|
-
await ln.goto(path ?? "/")
|
|
52
|
-
return ln
|
|
53
|
-
}),
|
|
54
|
-
})
|
|
36
|
+
const root = fileURLToPath(new URL("./fixtures/basics/", import.meta.url))
|
|
55
37
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
38
|
+
let server: Server | null = null
|
|
39
|
+
const test = baseTest.extend<{
|
|
40
|
+
lightnet: (path?: string) => Promise<LightNetPage>
|
|
41
|
+
}>({
|
|
42
|
+
lightnet: ({ page }, use) =>
|
|
43
|
+
use(async (path) => {
|
|
44
|
+
if (!server) {
|
|
45
|
+
await build({ logLevel: "error", root })
|
|
46
|
+
server = await preview({ logLevel: "error", root })
|
|
47
|
+
}
|
|
48
|
+
const ln = new LightNetPage(server, page)
|
|
49
|
+
await ln.goto(path ?? "/")
|
|
50
|
+
return ln
|
|
51
|
+
}),
|
|
52
|
+
})
|
|
59
53
|
|
|
60
|
-
|
|
54
|
+
const teardown = async () => {
|
|
55
|
+
await server?.stop()
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
// A Playwright test fixture accessible from within all tests.
|
|
@@ -78,3 +73,5 @@ class LightNetPage {
|
|
|
78
73
|
}
|
|
79
74
|
|
|
80
75
|
type Server = Awaited<ReturnType<typeof preview>>
|
|
76
|
+
|
|
77
|
+
export { teardown, test }
|
|
@@ -10,9 +10,9 @@ case `uname` in
|
|
|
10
10
|
esac
|
|
11
11
|
|
|
12
12
|
if [ -z "$NODE_PATH" ]; then
|
|
13
|
-
export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.15.
|
|
13
|
+
export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.15.8_@types+node@24.10.1_jiti@2.4.2_lightningcss@1.29.1_rollup@4.53.2_terser@5.39.0_typescript@5.9.3_yaml@2.8.1/node_modules/astro/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.15.8_@types+node@24.10.1_jiti@2.4.2_lightningcss@1.29.1_rollup@4.53.2_terser@5.39.0_typescript@5.9.3_yaml@2.8.1/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules"
|
|
14
14
|
else
|
|
15
|
-
export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.15.
|
|
15
|
+
export NODE_PATH="/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.15.8_@types+node@24.10.1_jiti@2.4.2_lightningcss@1.29.1_rollup@4.53.2_terser@5.39.0_typescript@5.9.3_yaml@2.8.1/node_modules/astro/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/astro@5.15.8_@types+node@24.10.1_jiti@2.4.2_lightningcss@1.29.1_rollup@4.53.2_terser@5.39.0_typescript@5.9.3_yaml@2.8.1/node_modules:/home/runner/work/LightNet/LightNet/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
16
16
|
fi
|
|
17
17
|
if [ -x "$basedir/node" ]; then
|
|
18
18
|
exec "$basedir/node" "$basedir/../astro/astro.js" "$@"
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
"@astrojs/react": "^4.4.2",
|
|
8
8
|
"@astrojs/tailwind": "^6.0.2",
|
|
9
9
|
"@lightnet/decap-admin": "^3.1.4",
|
|
10
|
-
"astro": "^5.15.
|
|
11
|
-
"lightnet": "^3.10.
|
|
10
|
+
"astro": "^5.15.8",
|
|
11
|
+
"lightnet": "^3.10.5",
|
|
12
12
|
"react": "^19.2.0",
|
|
13
13
|
"react-dom": "^19.2.0",
|
|
14
14
|
"sharp": "^0.34.5",
|
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
"language": "en",
|
|
10
10
|
"categories": ["christian-living"],
|
|
11
11
|
"collections": [{ "collection": "how-to-articles" }],
|
|
12
|
-
"description": "*How to: Faithful Freestyle* empowers you to express your Christianity through the unique and creative outlet of skateboarding. This book includes:\n\n
|
|
12
|
+
"description": "*How to: Faithful Freestyle* empowers you to express your Christianity through the unique and creative outlet of skateboarding. This book includes:\n\n* **Creative ways to incorporate faith** into your skating routines\n* **Stories of freestyle skaters** who honor God through their sport\n* **Practical advice on witnessing** to others in the skateboarding scene\n* **Inspirational devotions** designed for skaters\n\nEmbrace a faithful freestyle and let every trick and turn reflect your devotion to Christ."
|
|
13
13
|
}
|
|
@@ -11,5 +11,5 @@
|
|
|
11
11
|
"image": "./images/cover.jpg",
|
|
12
12
|
"language": "en",
|
|
13
13
|
"categories": ["christian-living"],
|
|
14
|
-
"description": "A vibrant collection of authentic skate park sounds to energize your projects and playlists.\n\n**Highlights:**\n
|
|
14
|
+
"description": "A vibrant collection of authentic skate park sounds to energize your projects and playlists.\n\n**Highlights:**\n* Real-world skating ambience\n* Perfect for creative mixes\n* Inspiring background audio for worship gatherings"
|
|
15
15
|
}
|
package/__e2e__/homepage.spec.ts
CHANGED
|
@@ -1,34 +1,32 @@
|
|
|
1
1
|
import { expect } from "@playwright/test"
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { test } from "./basics-fixture"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
test("Should have title set", async ({ page, startLightnet }) => {
|
|
8
|
-
await startLightnet()
|
|
5
|
+
test("Should have title set", async ({ page, lightnet }) => {
|
|
6
|
+
await lightnet()
|
|
9
7
|
await expect(page).toHaveTitle("Basic Test")
|
|
10
8
|
})
|
|
11
9
|
|
|
12
10
|
test("Should have header title that navigates to home page", async ({
|
|
13
11
|
page,
|
|
14
|
-
|
|
12
|
+
lightnet,
|
|
15
13
|
}) => {
|
|
16
|
-
const ln = await
|
|
14
|
+
const ln = await lightnet()
|
|
17
15
|
await page.getByRole("link", { name: "Basic Test" }).click()
|
|
18
16
|
|
|
19
17
|
await expect(page).toHaveURL(ln.resolveURL("/en/"))
|
|
20
18
|
})
|
|
21
19
|
|
|
22
|
-
test("Should have item section", async ({ page,
|
|
23
|
-
await
|
|
20
|
+
test("Should have item section", async ({ page, lightnet }) => {
|
|
21
|
+
await lightnet()
|
|
24
22
|
await expect(page.getByRole("heading", { name: "All items" })).toBeVisible()
|
|
25
23
|
})
|
|
26
24
|
|
|
27
25
|
test("Should navigate to search page from main menu", async ({
|
|
28
26
|
page,
|
|
29
|
-
|
|
27
|
+
lightnet,
|
|
30
28
|
}) => {
|
|
31
|
-
const ln = await
|
|
29
|
+
const ln = await lightnet()
|
|
32
30
|
await expect(
|
|
33
31
|
page.getByRole("button", { name: "Open Main Menu" }),
|
|
34
32
|
).toBeVisible()
|
|
@@ -42,8 +40,8 @@ test("Should navigate to search page from main menu", async ({
|
|
|
42
40
|
await expect(page.getByRole("heading", { name: "Search" })).toBeVisible()
|
|
43
41
|
})
|
|
44
42
|
|
|
45
|
-
test("Should switch languages", async ({ page,
|
|
46
|
-
const ln = await
|
|
43
|
+
test("Should switch languages", async ({ page, lightnet }) => {
|
|
44
|
+
const ln = await lightnet()
|
|
47
45
|
|
|
48
46
|
await page.getByLabel("Select language").click()
|
|
49
47
|
await page.getByRole("link", { name: "Deutsch" }).click()
|
|
@@ -57,9 +55,9 @@ test("Should switch languages", async ({ page, startLightnet }) => {
|
|
|
57
55
|
|
|
58
56
|
test("Should verify EN Detail media page url and title", async ({
|
|
59
57
|
page,
|
|
60
|
-
|
|
58
|
+
lightnet,
|
|
61
59
|
}) => {
|
|
62
|
-
const ln = await
|
|
60
|
+
const ln = await lightnet()
|
|
63
61
|
|
|
64
62
|
await page.getByRole("link", { name: "Faithful Freestyle" }).click()
|
|
65
63
|
await expect(
|
|
@@ -80,9 +78,9 @@ test("Should verify EN Detail media page url and title", async ({
|
|
|
80
78
|
|
|
81
79
|
test("Should verify DE Detail media page url and title", async ({
|
|
82
80
|
page,
|
|
83
|
-
|
|
81
|
+
lightnet,
|
|
84
82
|
}) => {
|
|
85
|
-
const ln = await
|
|
83
|
+
const ln = await lightnet()
|
|
86
84
|
|
|
87
85
|
await page.getByLabel("Select language").click()
|
|
88
86
|
await page.getByRole("link", { name: "Deutsch" }).click()
|
|
@@ -105,9 +103,9 @@ test("Should verify DE Detail media page url and title", async ({
|
|
|
105
103
|
|
|
106
104
|
test("Should show `Powered by LightNet` in footer", async ({
|
|
107
105
|
page,
|
|
108
|
-
|
|
106
|
+
lightnet,
|
|
109
107
|
}) => {
|
|
110
|
-
await
|
|
108
|
+
await lightnet()
|
|
111
109
|
|
|
112
110
|
const footerLink = page
|
|
113
111
|
.getByRole("contentinfo")
|
package/__e2e__/search.spec.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { expect } from "@playwright/test"
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
const test = lightnetTest("./fixtures/basics/")
|
|
3
|
+
import { test } from "./basics-fixture"
|
|
6
4
|
|
|
7
5
|
test("Search should have heading section and URL", async ({
|
|
8
6
|
page,
|
|
9
|
-
|
|
7
|
+
lightnet,
|
|
10
8
|
}) => {
|
|
11
|
-
const ln = await
|
|
9
|
+
const ln = await lightnet()
|
|
12
10
|
|
|
13
11
|
await page.getByLabel("Search").click()
|
|
14
12
|
await expect(page.getByRole("heading", { name: "Search" })).toBeVisible()
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "LightNet makes it easy to run your own digital media library.",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "3.10.
|
|
6
|
+
"version": "3.10.6",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/LightNetDev/lightnet",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"@hookform/resolvers": "^5.2.2",
|
|
51
51
|
"@iconify-json/mdi": "^1.2.3",
|
|
52
52
|
"@iconify/tailwind": "^1.2.0",
|
|
53
|
+
"@mdxeditor/editor": "^3.49.1",
|
|
53
54
|
"@tailwindcss/typography": "^0.5.19",
|
|
54
55
|
"@tanstack/react-virtual": "^3.13.12",
|
|
55
56
|
"daisyui": "^4.12.24",
|
|
@@ -64,9 +65,9 @@
|
|
|
64
65
|
"devDependencies": {
|
|
65
66
|
"@playwright/test": "^1.56.1",
|
|
66
67
|
"@types/node": "^22.19.1",
|
|
67
|
-
"@types/react": "^19.2.
|
|
68
|
+
"@types/react": "^19.2.5",
|
|
68
69
|
"typescript": "^5.9.3",
|
|
69
|
-
"vitest": "^4.0.
|
|
70
|
+
"vitest": "^4.0.9"
|
|
70
71
|
},
|
|
71
72
|
"engines": {
|
|
72
73
|
"node": ">=22"
|
package/playwright.config.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { defineConfig, devices } from "@playwright/test"
|
|
|
5
5
|
*/
|
|
6
6
|
export default defineConfig({
|
|
7
7
|
testDir: "./__e2e__",
|
|
8
|
+
globalTeardown: "./__e2e__/global.teardown.ts",
|
|
8
9
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
|
9
10
|
forbidOnly: !!process.env.CI,
|
|
10
11
|
/* Retry on CI only */
|
|
@@ -8,12 +8,14 @@ import { useFieldError } from "./hooks/use-field-error"
|
|
|
8
8
|
export default function Input<TFieldValues extends FieldValues>({
|
|
9
9
|
name,
|
|
10
10
|
label,
|
|
11
|
+
defaultValue,
|
|
11
12
|
hint,
|
|
12
13
|
control,
|
|
13
14
|
type = "text",
|
|
14
15
|
}: {
|
|
15
16
|
name: Path<TFieldValues>
|
|
16
17
|
label: string
|
|
18
|
+
defaultValue?: string
|
|
17
19
|
hint?: string
|
|
18
20
|
control: Control<TFieldValues>
|
|
19
21
|
type?: "text" | "date"
|
|
@@ -26,6 +28,7 @@ export default function Input<TFieldValues extends FieldValues>({
|
|
|
26
28
|
className={`dy-input dy-input-bordered shadow-inner ${errorMessage ? "dy-input-error" : ""}`}
|
|
27
29
|
type={type}
|
|
28
30
|
id={name}
|
|
31
|
+
defaultValue={defaultValue}
|
|
29
32
|
aria-invalid={!!errorMessage}
|
|
30
33
|
{...control.register(name)}
|
|
31
34
|
/>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import "@mdxeditor/editor/style.css"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
BlockTypeSelect,
|
|
5
|
+
BoldItalicUnderlineToggles,
|
|
6
|
+
CreateLink,
|
|
7
|
+
diffSourcePlugin,
|
|
8
|
+
DiffSourceToggleWrapper,
|
|
9
|
+
headingsPlugin,
|
|
10
|
+
linkDialogPlugin,
|
|
11
|
+
linkPlugin,
|
|
12
|
+
listsPlugin,
|
|
13
|
+
ListsToggle,
|
|
14
|
+
MDXEditor,
|
|
15
|
+
quotePlugin,
|
|
16
|
+
toolbarPlugin,
|
|
17
|
+
UndoRedo,
|
|
18
|
+
} from "@mdxeditor/editor"
|
|
19
|
+
import {
|
|
20
|
+
type Control,
|
|
21
|
+
Controller,
|
|
22
|
+
type FieldValues,
|
|
23
|
+
type Path,
|
|
24
|
+
} from "react-hook-form"
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* IMPORTANT: Do not import this component directly. It is
|
|
28
|
+
* very big. Use it with React lazy loading.
|
|
29
|
+
*/
|
|
30
|
+
export default function LazyLoadedMarkdownEditor<
|
|
31
|
+
TFieldValues extends FieldValues,
|
|
32
|
+
>({
|
|
33
|
+
control,
|
|
34
|
+
defaultValue,
|
|
35
|
+
name,
|
|
36
|
+
}: {
|
|
37
|
+
name: Path<TFieldValues>
|
|
38
|
+
control: Control<TFieldValues>
|
|
39
|
+
defaultValue?: string
|
|
40
|
+
}) {
|
|
41
|
+
return (
|
|
42
|
+
<Controller
|
|
43
|
+
control={control}
|
|
44
|
+
name={name}
|
|
45
|
+
render={({ field: { onBlur, onChange, value, ref } }) => (
|
|
46
|
+
<MDXEditor
|
|
47
|
+
markdown={value ?? ""}
|
|
48
|
+
onBlur={onBlur}
|
|
49
|
+
onChange={onChange}
|
|
50
|
+
contentEditableClassName="prose bg-gray-50 h-80 w-full max-w-full overflow-y-auto"
|
|
51
|
+
ref={ref}
|
|
52
|
+
plugins={[
|
|
53
|
+
headingsPlugin(),
|
|
54
|
+
listsPlugin(),
|
|
55
|
+
linkPlugin(),
|
|
56
|
+
linkDialogPlugin(),
|
|
57
|
+
diffSourcePlugin({
|
|
58
|
+
viewMode: "rich-text",
|
|
59
|
+
diffMarkdown: defaultValue,
|
|
60
|
+
}),
|
|
61
|
+
quotePlugin(),
|
|
62
|
+
toolbarPlugin({
|
|
63
|
+
toolbarContents: () => (
|
|
64
|
+
<DiffSourceToggleWrapper>
|
|
65
|
+
<UndoRedo />
|
|
66
|
+
<BoldItalicUnderlineToggles />
|
|
67
|
+
<BlockTypeSelect />
|
|
68
|
+
<ListsToggle options={["bullet", "number"]} />
|
|
69
|
+
<CreateLink />
|
|
70
|
+
</DiffSourceToggleWrapper>
|
|
71
|
+
),
|
|
72
|
+
}),
|
|
73
|
+
]}
|
|
74
|
+
/>
|
|
75
|
+
)}
|
|
76
|
+
/>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { lazy, Suspense } from "react"
|
|
2
|
+
import { type Control, type FieldValues, type Path } from "react-hook-form"
|
|
3
|
+
|
|
4
|
+
import ErrorMessage from "./atoms/ErrorMessage"
|
|
5
|
+
import Hint from "./atoms/Hint"
|
|
6
|
+
import Legend from "./atoms/Legend"
|
|
7
|
+
import { useFieldError } from "./hooks/use-field-error"
|
|
8
|
+
|
|
9
|
+
const LazyLoadedMarkdownEditor = lazy(
|
|
10
|
+
() => import("./LazyLoadedMarkdownEditor"),
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
export default function MarkdownEditor<TFieldValues extends FieldValues>({
|
|
14
|
+
control,
|
|
15
|
+
defaultValue,
|
|
16
|
+
name,
|
|
17
|
+
label,
|
|
18
|
+
hint,
|
|
19
|
+
}: {
|
|
20
|
+
name: Path<TFieldValues>
|
|
21
|
+
label: string
|
|
22
|
+
hint?: string
|
|
23
|
+
control: Control<TFieldValues>
|
|
24
|
+
defaultValue?: string
|
|
25
|
+
}) {
|
|
26
|
+
const errorMessage = useFieldError({ control, name })
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<fieldset key={name}>
|
|
30
|
+
<Legend label={label} />
|
|
31
|
+
<div
|
|
32
|
+
className={`overflow-hidden rounded-lg border border-gray-300 shadow-sm ${errorMessage ? "border-rose-800" : ""}`}
|
|
33
|
+
>
|
|
34
|
+
<Suspense
|
|
35
|
+
fallback={
|
|
36
|
+
<div className="h-[22.75rem] w-full bg-gray-50">
|
|
37
|
+
<div className="h-10 bg-gray-100"></div>
|
|
38
|
+
</div>
|
|
39
|
+
}
|
|
40
|
+
>
|
|
41
|
+
<LazyLoadedMarkdownEditor
|
|
42
|
+
control={control as Control<any>}
|
|
43
|
+
name={name}
|
|
44
|
+
defaultValue={defaultValue}
|
|
45
|
+
/>
|
|
46
|
+
</Suspense>
|
|
47
|
+
</div>
|
|
48
|
+
<ErrorMessage message={errorMessage} />
|
|
49
|
+
<Hint label={hint} />
|
|
50
|
+
</fieldset>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -9,12 +9,14 @@ export default function Select<TFieldValues extends FieldValues>({
|
|
|
9
9
|
name,
|
|
10
10
|
label,
|
|
11
11
|
control,
|
|
12
|
+
defaultValue,
|
|
12
13
|
hint,
|
|
13
14
|
options,
|
|
14
15
|
}: {
|
|
15
16
|
name: Path<TFieldValues>
|
|
16
17
|
label: string
|
|
17
18
|
hint?: string
|
|
19
|
+
defaultValue?: string
|
|
18
20
|
control: Control<TFieldValues>
|
|
19
21
|
options: { id: string; labelText?: string }[]
|
|
20
22
|
}) {
|
|
@@ -26,6 +28,7 @@ export default function Select<TFieldValues extends FieldValues>({
|
|
|
26
28
|
{...control.register(name)}
|
|
27
29
|
id={name}
|
|
28
30
|
aria-invalid={!!errorMessage}
|
|
31
|
+
defaultValue={defaultValue}
|
|
29
32
|
className={`dy-select dy-select-bordered text-base shadow-sm ${errorMessage ? "dy-select-error" : ""}`}
|
|
30
33
|
>
|
|
31
34
|
{options.map(({ id, labelText }) => (
|
|
@@ -15,7 +15,7 @@ export default function Label({
|
|
|
15
15
|
return (
|
|
16
16
|
<label
|
|
17
17
|
htmlFor={htmlFor}
|
|
18
|
-
className={`font-bold uppercase text-gray-
|
|
18
|
+
className={`font-bold uppercase text-gray-600 ${size === "sm" ? "pb-2 text-sm" : "pb-1 text-xs"} ${className}`}
|
|
19
19
|
>
|
|
20
20
|
{t(label)}
|
|
21
21
|
</label>
|
|
@@ -12,7 +12,7 @@ export default function Legend({
|
|
|
12
12
|
const { t } = useI18n()
|
|
13
13
|
return (
|
|
14
14
|
<legend
|
|
15
|
-
className={`pb-2 font-bold uppercase text-gray-
|
|
15
|
+
className={`pb-2 font-bold uppercase text-gray-600 ${size === "sm" ? "text-sm" : "text-xs"} ${className}`}
|
|
16
16
|
>
|
|
17
17
|
{t(label)}
|
|
18
18
|
</legend>
|
|
@@ -16,6 +16,7 @@ ln.admin.back-to-details-page: Back to details page
|
|
|
16
16
|
ln.admin.title: Title
|
|
17
17
|
ln.admin.common-id: Common ID
|
|
18
18
|
ln.admin.authors: Authors
|
|
19
|
+
ln.admin.description: Description
|
|
19
20
|
ln.admin.created-on: Created on
|
|
20
21
|
ln.admin.created-on-hint: When has this item been created on this library?
|
|
21
22
|
ln.admin.common-id-hint: The English title, all lowercase, words separated with hyphens.
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
I18nContext,
|
|
8
8
|
} from "../../../i18n/react/i18n-context"
|
|
9
9
|
import Input from "../../components/form/Input"
|
|
10
|
+
import MarkdownEditor from "../../components/form/MarkdownEditor"
|
|
10
11
|
import Select from "../../components/form/Select"
|
|
11
12
|
import SubmitButton from "../../components/form/SubmitButton"
|
|
12
13
|
import { type MediaItem, mediaItemSchema } from "../../types/media-item"
|
|
@@ -35,6 +36,8 @@ export default function EditForm({
|
|
|
35
36
|
collections: SelectOption[]
|
|
36
37
|
}) {
|
|
37
38
|
const { handleSubmit, control } = useForm({
|
|
39
|
+
// Provide per-input defaults so SSG prerender matches, but keep a full
|
|
40
|
+
// defaultValues object here because useFieldArray does not accept default values.
|
|
38
41
|
defaultValues: mediaItem,
|
|
39
42
|
mode: "onTouched",
|
|
40
43
|
shouldFocusError: true,
|
|
@@ -52,35 +55,58 @@ export default function EditForm({
|
|
|
52
55
|
<SubmitButton control={control} />
|
|
53
56
|
</div>
|
|
54
57
|
|
|
55
|
-
<Input
|
|
58
|
+
<Input
|
|
59
|
+
name="title"
|
|
60
|
+
label="ln.admin.title"
|
|
61
|
+
control={control}
|
|
62
|
+
defaultValue={mediaItem.title}
|
|
63
|
+
/>
|
|
56
64
|
<Input
|
|
57
65
|
name="commonId"
|
|
58
66
|
label="ln.admin.common-id"
|
|
59
67
|
hint="ln.admin.common-id-hint"
|
|
60
68
|
control={control}
|
|
69
|
+
defaultValue={mediaItem.commonId}
|
|
61
70
|
/>
|
|
62
71
|
<Select
|
|
63
72
|
name="type"
|
|
64
73
|
label="ln.type"
|
|
65
74
|
options={mediaTypes}
|
|
66
75
|
control={control}
|
|
76
|
+
defaultValue={mediaItem.type}
|
|
67
77
|
/>
|
|
68
78
|
<Select
|
|
69
79
|
name="language"
|
|
70
80
|
label="ln.language"
|
|
81
|
+
defaultValue={mediaItem.language}
|
|
71
82
|
options={languages}
|
|
72
83
|
control={control}
|
|
73
84
|
/>
|
|
74
|
-
<Authors control={control} />
|
|
85
|
+
<Authors control={control} defaultValue={mediaItem.authors} />
|
|
75
86
|
<Input
|
|
76
87
|
name="dateCreated"
|
|
77
88
|
label="ln.admin.created-on"
|
|
78
89
|
hint="ln.admin.created-on-hint"
|
|
79
90
|
type="date"
|
|
91
|
+
defaultValue={mediaItem.dateCreated}
|
|
92
|
+
control={control}
|
|
93
|
+
/>
|
|
94
|
+
<Categories
|
|
95
|
+
categories={categories}
|
|
96
|
+
control={control}
|
|
97
|
+
defaultValue={mediaItem.categories}
|
|
98
|
+
/>
|
|
99
|
+
<Collections
|
|
100
|
+
collections={collections}
|
|
101
|
+
control={control}
|
|
102
|
+
defaultValue={mediaItem.collections}
|
|
103
|
+
/>
|
|
104
|
+
<MarkdownEditor
|
|
80
105
|
control={control}
|
|
106
|
+
name="description"
|
|
107
|
+
label="ln.admin.description"
|
|
108
|
+
defaultValue={mediaItem.description}
|
|
81
109
|
/>
|
|
82
|
-
<Categories categories={categories} control={control} />
|
|
83
|
-
<Collections collections={collections} control={control} />
|
|
84
110
|
|
|
85
111
|
<SubmitButton className="self-end" control={control} />
|
|
86
112
|
</form>
|
|
@@ -61,8 +61,8 @@ const languages = config.languages.map(({ code, label }) => ({
|
|
|
61
61
|
<Page mainClass="bg-slate-500">
|
|
62
62
|
<div class="mx-auto block max-w-screen-md px-4 md:px-8">
|
|
63
63
|
<a
|
|
64
|
-
class="block
|
|
65
|
-
href=`/${Astro.currentLocale}/media
|
|
64
|
+
class="block pb-4 pt-8 text-gray-200 underline"
|
|
65
|
+
href=`/${Astro.currentLocale}/media/${mediaId}`
|
|
66
66
|
>{t("ln.admin.back-to-details-page")}</a
|
|
67
67
|
>
|
|
68
68
|
</div>
|
|
@@ -5,13 +5,25 @@ import DynamicArray from "../../../components/form/DynamicArray"
|
|
|
5
5
|
import { useFieldError } from "../../../components/form/hooks/use-field-error"
|
|
6
6
|
import type { MediaItem } from "../../../types/media-item"
|
|
7
7
|
|
|
8
|
-
export default function Authors({
|
|
8
|
+
export default function Authors({
|
|
9
|
+
control,
|
|
10
|
+
defaultValue,
|
|
11
|
+
}: {
|
|
12
|
+
control: Control<MediaItem>
|
|
13
|
+
defaultValue: MediaItem["authors"]
|
|
14
|
+
}) {
|
|
9
15
|
return (
|
|
10
16
|
<DynamicArray
|
|
11
17
|
control={control}
|
|
12
18
|
name="authors"
|
|
13
19
|
label="ln.admin.authors"
|
|
14
|
-
renderElement={(index) =>
|
|
20
|
+
renderElement={(index) => (
|
|
21
|
+
<AuthorInput
|
|
22
|
+
index={index}
|
|
23
|
+
control={control}
|
|
24
|
+
defaultValue={defaultValue[index]?.value}
|
|
25
|
+
/>
|
|
26
|
+
)}
|
|
15
27
|
addButton={{
|
|
16
28
|
label: "ln.admin.add-author",
|
|
17
29
|
onClick: (append, index) =>
|
|
@@ -24,9 +36,11 @@ export default function Authors({ control }: { control: Control<MediaItem> }) {
|
|
|
24
36
|
function AuthorInput({
|
|
25
37
|
index,
|
|
26
38
|
control,
|
|
39
|
+
defaultValue,
|
|
27
40
|
}: {
|
|
28
41
|
index: number
|
|
29
42
|
control: Control<MediaItem>
|
|
43
|
+
defaultValue?: string
|
|
30
44
|
}) {
|
|
31
45
|
const name = `authors.${index}.value` as const
|
|
32
46
|
const errorMessage = useFieldError({ name, control })
|
|
@@ -35,6 +49,7 @@ function AuthorInput({
|
|
|
35
49
|
<input
|
|
36
50
|
className={`dy-input dy-input-bordered shadow-inner ${errorMessage ? "dy-input-error" : ""}`}
|
|
37
51
|
aria-invalid={!!errorMessage}
|
|
52
|
+
defaultValue={defaultValue}
|
|
38
53
|
{...control.register(name)}
|
|
39
54
|
/>
|
|
40
55
|
<ErrorMessage message={errorMessage} />
|
|
@@ -8,8 +8,10 @@ import type { MediaItem } from "../../../types/media-item"
|
|
|
8
8
|
export default function Categories({
|
|
9
9
|
control,
|
|
10
10
|
categories,
|
|
11
|
+
defaultValue,
|
|
11
12
|
}: {
|
|
12
13
|
control: Control<MediaItem>
|
|
14
|
+
defaultValue: MediaItem["categories"]
|
|
13
15
|
categories: { id: string; labelText: string }[]
|
|
14
16
|
}) {
|
|
15
17
|
return (
|
|
@@ -22,6 +24,7 @@ export default function Categories({
|
|
|
22
24
|
categories={categories}
|
|
23
25
|
control={control}
|
|
24
26
|
index={index}
|
|
27
|
+
defaultValue={defaultValue[index]?.value}
|
|
25
28
|
/>
|
|
26
29
|
)}
|
|
27
30
|
addButton={{
|
|
@@ -36,10 +39,12 @@ export default function Categories({
|
|
|
36
39
|
function CategorySelect({
|
|
37
40
|
control,
|
|
38
41
|
categories,
|
|
42
|
+
defaultValue,
|
|
39
43
|
index,
|
|
40
44
|
}: {
|
|
41
45
|
control: Control<MediaItem>
|
|
42
46
|
categories: { id: string; labelText: string }[]
|
|
47
|
+
defaultValue?: string
|
|
43
48
|
index: number
|
|
44
49
|
}) {
|
|
45
50
|
const name = `categories.${index}.value` as const
|
|
@@ -49,6 +54,7 @@ function CategorySelect({
|
|
|
49
54
|
<select
|
|
50
55
|
{...control.register(name)}
|
|
51
56
|
id={name}
|
|
57
|
+
defaultValue={defaultValue}
|
|
52
58
|
aria-invalid={!!errorMessage}
|
|
53
59
|
className={`dy-select dy-select-bordered text-base shadow-sm ${errorMessage ? "dy-select-error" : ""}`}
|
|
54
60
|
>
|
|
@@ -9,9 +9,11 @@ import type { MediaItem } from "../../../types/media-item"
|
|
|
9
9
|
export default function Collections({
|
|
10
10
|
control,
|
|
11
11
|
collections,
|
|
12
|
+
defaultValue,
|
|
12
13
|
}: {
|
|
13
14
|
control: Control<MediaItem>
|
|
14
15
|
collections: { id: string; labelText: string }[]
|
|
16
|
+
defaultValue: MediaItem["collections"]
|
|
15
17
|
}) {
|
|
16
18
|
return (
|
|
17
19
|
<DynamicArray
|
|
@@ -24,8 +26,13 @@ export default function Collections({
|
|
|
24
26
|
collections={collections}
|
|
25
27
|
control={control}
|
|
26
28
|
index={index}
|
|
29
|
+
defaultValue={defaultValue[index]?.collection}
|
|
30
|
+
/>
|
|
31
|
+
<CollectionIndex
|
|
32
|
+
control={control}
|
|
33
|
+
index={index}
|
|
34
|
+
defaultValue={defaultValue[index]?.index}
|
|
27
35
|
/>
|
|
28
|
-
<CollectionIndex control={control} index={index} />
|
|
29
36
|
</div>
|
|
30
37
|
)}
|
|
31
38
|
addButton={{
|
|
@@ -44,9 +51,11 @@ function CollectionSelect({
|
|
|
44
51
|
control,
|
|
45
52
|
collections,
|
|
46
53
|
index,
|
|
54
|
+
defaultValue,
|
|
47
55
|
}: {
|
|
48
56
|
control: Control<MediaItem>
|
|
49
57
|
collections: { id: string; labelText: string }[]
|
|
58
|
+
defaultValue?: string
|
|
50
59
|
index: number
|
|
51
60
|
}) {
|
|
52
61
|
const name = `collections.${index}.collection` as const
|
|
@@ -57,6 +66,7 @@ function CollectionSelect({
|
|
|
57
66
|
<select
|
|
58
67
|
{...control.register(name)}
|
|
59
68
|
id={name}
|
|
69
|
+
defaultValue={defaultValue}
|
|
60
70
|
aria-invalid={!!errorMessage}
|
|
61
71
|
className={`dy-select dy-select-bordered text-base shadow-sm ${errorMessage ? "dy-select-error" : ""}`}
|
|
62
72
|
>
|
|
@@ -74,9 +84,11 @@ function CollectionSelect({
|
|
|
74
84
|
function CollectionIndex({
|
|
75
85
|
control,
|
|
76
86
|
index,
|
|
87
|
+
defaultValue,
|
|
77
88
|
}: {
|
|
78
89
|
control: Control<MediaItem>
|
|
79
90
|
index: number
|
|
91
|
+
defaultValue?: number
|
|
80
92
|
}) {
|
|
81
93
|
const name = `collections.${index}.index` as const
|
|
82
94
|
const errorMessage = useFieldError({ name, control })
|
|
@@ -92,6 +104,8 @@ function CollectionIndex({
|
|
|
92
104
|
className={`dy-input dy-input-bordered shadow-inner ${errorMessage ? "dy-input-error" : ""}`}
|
|
93
105
|
aria-invalid={!!errorMessage}
|
|
94
106
|
type="number"
|
|
107
|
+
id={name}
|
|
108
|
+
defaultValue={defaultValue}
|
|
95
109
|
step={1}
|
|
96
110
|
{...control.register(name, {
|
|
97
111
|
setValueAs: (value) => (value === "" ? undefined : Number(value)),
|