@nocobase/app-template-default 1.0.0-beta.68 → 1.0.0-beta.70
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/.agents/rules/components.md +5 -1
- package/.agents/rules/shell.md +3 -1
- package/AGENTS.md +7 -3
- package/Dockerfile.dockerignore +0 -1
- package/README.MD +3 -1
- package/client/app.ts +15 -0
- package/client/components/data-table/column-header.tsx +91 -0
- package/client/components/data-table/data-table.tsx +216 -0
- package/client/components/data-table/index.ts +13 -0
- package/client/components/data-table/pagination.tsx +165 -0
- package/client/components/data-table/view-options.tsx +77 -0
- package/client/components/flow-diagram-css.d.ts +2 -0
- package/client/components/flow-diagram.tsx +674 -0
- package/client/components/flow-layout.ts +250 -0
- package/client/components/lifecycle-diagram-model.ts +217 -0
- package/client/components/lifecycle-diagram.tsx +310 -0
- package/client/components/permission-editor.tsx +589 -0
- package/client/components/permission-filter.tsx +455 -0
- package/client/components/route-overlay.tsx +26 -13
- package/client/components/ui/checkbox.tsx +25 -0
- package/client/components/ui/sidebar.tsx +1 -4
- package/client/components/ui/switch.tsx +30 -0
- package/client/components/ui/table.tsx +113 -0
- package/client/extensions/nocobase-workflow-management/README.md +70 -0
- package/client/extensions/nocobase-workflow-management/definition-list.tsx +148 -0
- package/client/extensions/nocobase-workflow-management/execution-detail.tsx +230 -0
- package/client/extensions/nocobase-workflow-management/execution-list.tsx +175 -0
- package/client/extensions/nocobase-workflow-management/layout.tsx +95 -0
- package/client/layouts/components/app-sidebar.tsx +1 -1
- package/client/layouts/use-sidebar-preference.ts +62 -42
- package/client/locales/system/en-US.ts +29 -0
- package/client/locales/system/settings-en-US.ts +142 -0
- package/client/locales/system/settings-zh-CN.ts +136 -0
- package/client/locales/system/zh-CN.ts +29 -0
- package/client/pages/auth/auth-page.css +15 -0
- package/client/pages/auth/login.tsx +1 -1
- package/client/pages/auth/shared.tsx +3 -1
- package/client/pages/settings/hooks.tsx +137 -0
- package/client/pages/settings/page-permission-titles.ts +7 -0
- package/client/pages/settings/page-permissions.ts +42 -0
- package/client/pages/settings/permission-filters.tsx +119 -0
- package/client/pages/settings/permissions-model.ts +260 -0
- package/client/pages/settings/role-form.tsx +405 -0
- package/client/pages/settings/roles.tsx +163 -0
- package/client/pages/settings/routes.ts +79 -0
- package/client/pages/settings/shared.tsx +119 -0
- package/client/pages/settings/user-detail.tsx +268 -0
- package/client/pages/settings/user-form.tsx +427 -0
- package/client/pages/settings/users.tsx +451 -0
- package/client/routing/app-router.tsx +4 -1
- package/client/routing/session-preparation.tsx +65 -0
- package/client/styles.css +4 -7
- package/package.json +27 -24
- package/tests/components/app-layout-routes.test.tsx +14 -7
- package/tests/components/data-table.test.tsx +127 -0
- package/tests/components/layout-i18n.test.tsx +8 -1
- package/tests/components/navigation-menu.test.tsx +24 -14
- package/tests/components/permission-editor.test.tsx +477 -0
- package/tests/components/permission-filter-binding.test.tsx +150 -0
- package/tests/components/permission-filter.test.tsx +202 -0
- package/tests/components/settings.test.tsx +294 -0
- package/tests/components/sidebar-permissions.test.tsx +12 -4
- package/tests/components/sidebar-preference.test.tsx +209 -14
- package/tests/components/user-permissions.test.tsx +142 -0
- package/tests/fixtures/theme-tokens.tsx +9 -1
- package/tests/logic/asset-url-build.test.ts +0 -1
- package/tests/logic/client-routes.test.ts +40 -0
- package/tests/logic/client-shell.test.tsx +10 -2
- package/tests/logic/config-example.test.ts +30 -0
- package/tests/logic/dev-client-watch.test.ts +8 -1
- package/tests/logic/page-permission-titles.test.ts +223 -0
- package/tests/logic/route-overlay.test.tsx +42 -0
- package/tests/logic/session-preparation.test.tsx +223 -0
- package/tests/logic/settings-permissions.test.ts +168 -0
- package/vite.config.ts +0 -22
- package/client/agent-annotations-host.ts +0 -25
|
@@ -10,6 +10,10 @@ import { MemoryRouter, Route, Routes } from 'react-router';
|
|
|
10
10
|
import { afterEach, beforeEach, expect, it, vi } from 'vitest';
|
|
11
11
|
|
|
12
12
|
import { AppLayout } from '../../client/layouts/app-layout.js';
|
|
13
|
+
import {
|
|
14
|
+
createAppSidebarPreferenceStore,
|
|
15
|
+
SidebarPreferenceProvider,
|
|
16
|
+
} from '../../client/layouts/use-sidebar-preference.js';
|
|
13
17
|
import enUS from '../../client/locales/en-US.js';
|
|
14
18
|
|
|
15
19
|
// Breadcrumb titles are route data, not keys any namespace owns: the trail translates each through its package's
|
|
@@ -87,14 +91,17 @@ function OrderPage(): ReactElement {
|
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
function renderLayout(page: ReactElement): HTMLElement {
|
|
94
|
+
const sidebarPreference = createAppSidebarPreferenceStore('/test-app');
|
|
90
95
|
render(
|
|
91
|
-
<
|
|
92
|
-
<
|
|
93
|
-
<
|
|
94
|
-
<Route
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
<SidebarPreferenceProvider value={sidebarPreference}>
|
|
97
|
+
<MemoryRouter initialEntries={['/orders/42']}>
|
|
98
|
+
<Routes>
|
|
99
|
+
<Route element={<AppLayout routes={routes} />}>
|
|
100
|
+
<Route path='/orders/:id' element={page} />
|
|
101
|
+
</Route>
|
|
102
|
+
</Routes>
|
|
103
|
+
</MemoryRouter>
|
|
104
|
+
</SidebarPreferenceProvider>,
|
|
98
105
|
{ wrapper: I18n },
|
|
99
106
|
);
|
|
100
107
|
return screen.getByRole('banner');
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { renderWithApp } from '@nocobase/app-testing/client';
|
|
2
|
+
import type { ColumnDef, PaginationState } from '@tanstack/react-table';
|
|
3
|
+
import { screen, within } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
import { describe, it, expect } from 'vitest';
|
|
7
|
+
import {
|
|
8
|
+
DataTable,
|
|
9
|
+
DataTableColumnHeader,
|
|
10
|
+
DataTableViewOptions,
|
|
11
|
+
} from '../../client/components/data-table/index.js';
|
|
12
|
+
import enUS from '../../client/locales/en-US.js';
|
|
13
|
+
import zhCN from '../../client/locales/zh-CN.js';
|
|
14
|
+
const namespace = '@nocobase/datatable-test';
|
|
15
|
+
const namespaces = {
|
|
16
|
+
[namespace]: {
|
|
17
|
+
'en-US': async () => ({ default: enUS }),
|
|
18
|
+
'zh-CN': async () => ({ default: zhCN }),
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
type Item = { id: string; amount: number };
|
|
22
|
+
const rows: Item[] = Array.from({ length: 12 }, (_, amount) => ({
|
|
23
|
+
id: `row-${amount}`,
|
|
24
|
+
amount,
|
|
25
|
+
}));
|
|
26
|
+
const columns: ColumnDef<Item>[] = [
|
|
27
|
+
{ accessorKey: 'id', header: 'ID', enableHiding: false },
|
|
28
|
+
{
|
|
29
|
+
accessorKey: 'amount',
|
|
30
|
+
header: ({ column }) => (
|
|
31
|
+
<DataTableColumnHeader column={column} title='Amount' />
|
|
32
|
+
),
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
describe('standard DataTable', () => {
|
|
36
|
+
it('uses real TanStack sorting, pagination and column visibility with restoration', async () => {
|
|
37
|
+
const user = userEvent.setup();
|
|
38
|
+
await renderWithApp(
|
|
39
|
+
<DataTable
|
|
40
|
+
columns={columns}
|
|
41
|
+
data={rows}
|
|
42
|
+
getRowId={(row) => row.id}
|
|
43
|
+
showSelectedCount={false}
|
|
44
|
+
toolbar={(table) => (
|
|
45
|
+
<DataTableViewOptions table={table} getColumnLabel={() => 'Amount'} />
|
|
46
|
+
)}
|
|
47
|
+
/>,
|
|
48
|
+
{ namespace, namespaces },
|
|
49
|
+
);
|
|
50
|
+
expect(screen.getByText('Page 1 of 2')).toBeVisible();
|
|
51
|
+
expect(screen.queryByText('row-10')).not.toBeInTheDocument();
|
|
52
|
+
await user.click(screen.getByRole('button', { name: 'Go to next page' }));
|
|
53
|
+
expect(screen.getByText('row-10')).toBeVisible();
|
|
54
|
+
await user.click(
|
|
55
|
+
screen.getByRole('button', { name: 'Go to previous page' }),
|
|
56
|
+
);
|
|
57
|
+
await user.click(
|
|
58
|
+
screen.getByRole('button', { name: 'Amount', exact: true }),
|
|
59
|
+
);
|
|
60
|
+
await user.click(
|
|
61
|
+
await screen.findByRole('menuitem', { name: 'Desc', exact: true }),
|
|
62
|
+
);
|
|
63
|
+
expect(
|
|
64
|
+
within(screen.getAllByRole('row')[1]!).getByText('row-11'),
|
|
65
|
+
).toBeVisible();
|
|
66
|
+
await user.click(screen.getByRole('button', { name: 'View', exact: true }));
|
|
67
|
+
await user.click(
|
|
68
|
+
await screen.findByRole('menuitemcheckbox', {
|
|
69
|
+
name: 'Amount',
|
|
70
|
+
exact: true,
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
expect(screen.getAllByRole('columnheader', { hidden: true })).toHaveLength(
|
|
74
|
+
1,
|
|
75
|
+
);
|
|
76
|
+
await user.click(
|
|
77
|
+
await screen.findByRole('menuitemcheckbox', {
|
|
78
|
+
name: 'Amount',
|
|
79
|
+
exact: true,
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
expect(screen.getByRole('columnheader', { name: 'Amount' })).toBeVisible();
|
|
83
|
+
});
|
|
84
|
+
it('preserves the server page and sort instead of applying client paging or sorting again', async () => {
|
|
85
|
+
const user = userEvent.setup();
|
|
86
|
+
const pageRows = rows.slice(0, 2);
|
|
87
|
+
function ServerPage() {
|
|
88
|
+
const [page, setPage] = useState<PaginationState>({
|
|
89
|
+
pageIndex: 2,
|
|
90
|
+
pageSize: 2,
|
|
91
|
+
});
|
|
92
|
+
return (
|
|
93
|
+
<DataTable
|
|
94
|
+
columns={columns}
|
|
95
|
+
data={pageRows}
|
|
96
|
+
manualPagination
|
|
97
|
+
rowCount={12}
|
|
98
|
+
paginationState={page}
|
|
99
|
+
onPaginationChange={setPage}
|
|
100
|
+
manualSorting
|
|
101
|
+
sortingState={[{ id: 'amount', desc: true }]}
|
|
102
|
+
showSelectedCount={false}
|
|
103
|
+
pageSizeOptions={[2]}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
await renderWithApp(<ServerPage />, { namespace, namespaces });
|
|
108
|
+
expect(screen.getByText('Page 3 of 6')).toBeVisible();
|
|
109
|
+
expect(screen.getByText('row-0')).toBeVisible();
|
|
110
|
+
expect(
|
|
111
|
+
within(screen.getAllByRole('row')[1]!).getByText('row-0'),
|
|
112
|
+
).toBeVisible();
|
|
113
|
+
await user.click(screen.getByRole('button', { name: 'Go to next page' }));
|
|
114
|
+
expect(screen.getByText('Page 4 of 6')).toBeVisible();
|
|
115
|
+
expect(screen.getByText('row-0')).toBeVisible();
|
|
116
|
+
});
|
|
117
|
+
it('keeps empty-state and paging controls localized in Chinese', async () => {
|
|
118
|
+
await renderWithApp(
|
|
119
|
+
<DataTable columns={columns} data={[]} showSelectedCount={false} />,
|
|
120
|
+
{ namespace, namespaces, locale: 'zh-CN' },
|
|
121
|
+
);
|
|
122
|
+
expect(screen.getByText(zhCN.dataTable.noResults)).toBeVisible();
|
|
123
|
+
expect(
|
|
124
|
+
screen.getByRole('button', { name: zhCN.dataTable.nextPage }),
|
|
125
|
+
).toBeDisabled();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -13,6 +13,10 @@ import { describe, expect, it, vi } from 'vitest';
|
|
|
13
13
|
|
|
14
14
|
import locales from '../../client/locales/index.js';
|
|
15
15
|
import { AppLayout } from '../../client/layouts/app-layout.js';
|
|
16
|
+
import {
|
|
17
|
+
createAppSidebarPreferenceStore,
|
|
18
|
+
SidebarPreferenceProvider,
|
|
19
|
+
} from '../../client/layouts/use-sidebar-preference.js';
|
|
16
20
|
vi.mock('@nocobase/app-plugin-i18n/client', () => ({
|
|
17
21
|
useSyncServerLocale: () => {},
|
|
18
22
|
}));
|
|
@@ -45,6 +49,7 @@ const overview: AppClientRegisteredRoute = {
|
|
|
45
49
|
};
|
|
46
50
|
|
|
47
51
|
async function setup(children: ReactNode) {
|
|
52
|
+
const sidebarPreference = createAppSidebarPreferenceStore('/test-app');
|
|
48
53
|
vi.stubGlobal('matchMedia', () => ({
|
|
49
54
|
matches: true,
|
|
50
55
|
addEventListener: vi.fn(),
|
|
@@ -64,7 +69,9 @@ async function setup(children: ReactNode) {
|
|
|
64
69
|
render(
|
|
65
70
|
<I18nProvider runtime={runtime}>
|
|
66
71
|
<ClientApplicationContext.Provider value={app}>
|
|
67
|
-
<
|
|
72
|
+
<SidebarPreferenceProvider value={sidebarPreference}>
|
|
73
|
+
<MemoryRouter initialEntries={['/']}>{children}</MemoryRouter>
|
|
74
|
+
</SidebarPreferenceProvider>
|
|
68
75
|
</ClientApplicationContext.Provider>
|
|
69
76
|
</I18nProvider>,
|
|
70
77
|
);
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createAppSidebarPreferenceStore,
|
|
3
|
+
SidebarPreferenceProvider,
|
|
4
|
+
} from '../../client/layouts/use-sidebar-preference.js';
|
|
1
5
|
import type { AppClientRegisteredRoute } from '@nocobase/app-client/plugins';
|
|
2
6
|
import {
|
|
3
7
|
act,
|
|
@@ -38,11 +42,15 @@ function I18n({ children }: { readonly children: ReactNode }) {
|
|
|
38
42
|
return <TestI18nProvider runtime={runtime}>{children}</TestI18nProvider>;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
const
|
|
45
|
+
const sidebarPreference = createAppSidebarPreferenceStore('/main');
|
|
46
|
+
|
|
47
|
+
const preferenceKey = 'nocobase:sidebar:collapsed:app:/main';
|
|
42
48
|
|
|
43
49
|
function setCollapsed(collapsed: boolean) {
|
|
44
50
|
localStorage.setItem(preferenceKey, String(collapsed));
|
|
45
|
-
window.dispatchEvent(
|
|
51
|
+
window.dispatchEvent(
|
|
52
|
+
new Event('nocobase:sidebar-preference-change:app:/main'),
|
|
53
|
+
);
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
function page(name: string): AppClientRegisteredRoute {
|
|
@@ -69,18 +77,20 @@ function shell(
|
|
|
69
77
|
) {
|
|
70
78
|
return (
|
|
71
79
|
<MemoryRouter>
|
|
72
|
-
<
|
|
73
|
-
<
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
<SidebarPreferenceProvider value={sidebarPreference}>
|
|
81
|
+
<AppSidebarProvider>
|
|
82
|
+
<AppSidebar label='Navigation'>
|
|
83
|
+
<NavigationMenu
|
|
84
|
+
items={items}
|
|
85
|
+
label='Menu'
|
|
86
|
+
selectedKey={selectedKey}
|
|
87
|
+
sections={sections}
|
|
88
|
+
badges={badges}
|
|
89
|
+
/>
|
|
90
|
+
</AppSidebar>
|
|
91
|
+
<AppSidebarToggle />
|
|
92
|
+
</AppSidebarProvider>
|
|
93
|
+
</SidebarPreferenceProvider>
|
|
84
94
|
</MemoryRouter>
|
|
85
95
|
);
|
|
86
96
|
}
|