proje-react-panel 1.11.0-beta.0 → 1.11.0
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 +22 -0
- package/dist/__tests__/components/list/LinkCell.test.d.ts +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/package.json +2 -2
- package/src/__tests__/components/list/LinkCell.test.tsx +70 -0
- package/src/api/CrudApi.ts +2 -2
- package/src/components/list/cells/LinkCell.tsx +21 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "proje-react-panel",
|
|
3
|
-
"version": "1.11.0
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "",
|
|
6
6
|
"author": "SEFA DEMİR",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"axios": ">=1.0.0",
|
|
74
74
|
"react": ">=19.0.0",
|
|
75
75
|
"react-hook-form": ">=7.54.2",
|
|
76
|
-
"react-router": "7.3.0",
|
|
76
|
+
"react-router": "^7.3.0",
|
|
77
77
|
"react-select": "^5.10.1",
|
|
78
78
|
"use-sync-external-store": ">=1.4.0",
|
|
79
79
|
"zustand": ">=5.0.3"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import { TextDecoder, TextEncoder } from 'util';
|
|
5
|
+
// react-router reads TextEncoder at import time and jsdom has none — set it up before the
|
|
6
|
+
// imports below pull the router in.
|
|
7
|
+
Object.assign(globalThis, { TextEncoder, TextDecoder });
|
|
8
|
+
|
|
9
|
+
import 'reflect-metadata';
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import { describe, expect, it } from '@jest/globals';
|
|
12
|
+
import { render } from '@testing-library/react';
|
|
13
|
+
import { MemoryRouter } from 'react-router';
|
|
14
|
+
import { LinkCell } from '../../../components/list/cells/LinkCell';
|
|
15
|
+
import { CellConfiguration } from '../../../decorators/list/Cell';
|
|
16
|
+
|
|
17
|
+
// `@LinkCell({ path: '/users/:id' })` satirin verisiyle dolmalı. Ikame bozulursa
|
|
18
|
+
// hucredeki her open/edit linki gorunuste calisir ama tiklaninca 404 verir —
|
|
19
|
+
// tarayicida fark edilmesi zor, o yuzden burada kilitli.
|
|
20
|
+
const hrefOf = (configuration: CellConfiguration, item: Record<string, unknown>) =>
|
|
21
|
+
render(
|
|
22
|
+
<MemoryRouter>
|
|
23
|
+
<table>
|
|
24
|
+
<tbody>
|
|
25
|
+
<tr>
|
|
26
|
+
<td>
|
|
27
|
+
<LinkCell configuration={configuration} item={item} />
|
|
28
|
+
</td>
|
|
29
|
+
</tr>
|
|
30
|
+
</tbody>
|
|
31
|
+
</table>
|
|
32
|
+
</MemoryRouter>
|
|
33
|
+
)
|
|
34
|
+
.container.querySelector('a')
|
|
35
|
+
?.getAttribute('href');
|
|
36
|
+
|
|
37
|
+
const linkCell = (over: Partial<CellConfiguration> = {}): CellConfiguration =>
|
|
38
|
+
({
|
|
39
|
+
name: 'open',
|
|
40
|
+
type: 'link',
|
|
41
|
+
placeHolder: 'open',
|
|
42
|
+
...over,
|
|
43
|
+
}) as CellConfiguration;
|
|
44
|
+
|
|
45
|
+
describe('LinkCell path parameters', () => {
|
|
46
|
+
it('substitutes a single :param from the row', () => {
|
|
47
|
+
expect(hrefOf(linkCell({ path: '/users/:id' } as never), { id: 42 })).toBe(
|
|
48
|
+
'/users/42'
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('substitutes every :param in the path', () => {
|
|
53
|
+
expect(
|
|
54
|
+
hrefOf(linkCell({ path: '/workspaces/:workspaceId/users/:id' } as never), {
|
|
55
|
+
workspaceId: 'ws-1',
|
|
56
|
+
id: 7,
|
|
57
|
+
})
|
|
58
|
+
).toBe('/workspaces/ws-1/users/7');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('leaves an unmatched :param in place instead of writing undefined', () => {
|
|
62
|
+
expect(hrefOf(linkCell({ path: '/users/:missing' } as never), { id: 1 })).toBe(
|
|
63
|
+
'/users/:missing'
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('passes a path with no parameters through untouched', () => {
|
|
68
|
+
expect(hrefOf(linkCell({ path: '/users' } as never), { id: 1 })).toBe('/users');
|
|
69
|
+
});
|
|
70
|
+
});
|
package/src/api/CrudApi.ts
CHANGED
|
@@ -64,7 +64,7 @@ export function update<T>(endpoint: string, key = 'id'): OnSubmitFN<T> {
|
|
|
64
64
|
export function updateFormData<T>(endpoint: string, key = 'id'): OnSubmitFN<T> {
|
|
65
65
|
return async (data: T | FormData): Promise<T> => {
|
|
66
66
|
const axiosInstance = getAxiosInstance();
|
|
67
|
-
const id = (data as
|
|
67
|
+
const id = (data as Record<string, unknown>)[key];
|
|
68
68
|
const response = await axiosInstance.put<T>(`/${endpoint}/${id}`, data, {
|
|
69
69
|
headers: {
|
|
70
70
|
'Content-Type': 'multipart/form-data',
|
|
@@ -89,7 +89,7 @@ export function remove<T>(endpoint: string, key = 'id'): (data: T) => Promise<vo
|
|
|
89
89
|
const id = (data as any)[key];
|
|
90
90
|
await axiosInstance
|
|
91
91
|
.delete<T>(`/${endpoint}/${id}`)
|
|
92
|
-
.then(
|
|
92
|
+
.then(res => res.data)
|
|
93
93
|
.catch((err: AxiosError) => {
|
|
94
94
|
const messageError = err.response?.data as { message: string };
|
|
95
95
|
if (messageError?.message) {
|
|
@@ -8,12 +8,32 @@ interface LinkCellProps<T> {
|
|
|
8
8
|
configuration: CellConfiguration;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* `@LinkCell({ path: '/users/:id' })` yazan model sahibi, satirin `id`'siyle dolmus
|
|
13
|
+
* bir adres bekliyor — router path'i zaten oyle okunuyor. Ikame edilmezse `:id`
|
|
14
|
+
* harfi harfine adrese giriyor ve hucredeki her open/edit linki 404'e cikiyor:
|
|
15
|
+
* gorunuste calisan, tiklaninca bozuk bir link. Eslesmeyen bir parametre oldugu
|
|
16
|
+
* gibi birakilir (silinmez) — boylece eksik alan gorunur kalir, sessizce
|
|
17
|
+
* `/users/undefined` uretilmez.
|
|
18
|
+
*/
|
|
19
|
+
function resolvePath<T>(path: string, item: T): string {
|
|
20
|
+
return path.replace(/:(\w+)/g, (match, key: string) => {
|
|
21
|
+
const value = (item as Record<string, unknown>)[key];
|
|
22
|
+
return value != null ? String(value) : match;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
11
26
|
export function LinkCell<T>({ item, configuration }: LinkCellProps<T>) {
|
|
12
27
|
const linkConfiguration = configuration as LinkCellConfiguration<T>;
|
|
13
28
|
const value = item[configuration.name as keyof T] ?? 'Link';
|
|
14
29
|
|
|
15
30
|
return (
|
|
16
|
-
<Link
|
|
31
|
+
<Link
|
|
32
|
+
to={resolvePath(
|
|
33
|
+
linkConfiguration.path ?? linkConfiguration.url ?? '',
|
|
34
|
+
item
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
17
37
|
{linkConfiguration.onClick ? (
|
|
18
38
|
<a
|
|
19
39
|
className="util-cell-link"
|