@gntik-ai/templates 0.9.0 → 0.10.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/dist/index.d.ts +13 -5
- package/dist/index.js +93 -67
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +1 -1
- package/src/keyboard-shortcuts/Page.test.tsx +39 -1
- package/src/keyboard-shortcuts/ShortcutSheet.tsx +3 -2
- package/src/keyboard-shortcuts/data.ts +28 -14
- package/src/resource-detail/Page.labels.test.tsx +67 -0
- package/src/resource-detail/Page.tsx +18 -7
- package/src/resource-detail/examples/Labels.tsx +18 -0
- package/src/resource-detail/examples/Spanish.tsx +11 -0
- package/src/resource-detail/template.meta.ts +1 -1
- package/src/settings-profile/fixtures/editable.snap +1 -1
- package/src/shared/ConsoleShell.test.tsx +13 -0
- package/src/shared/ConsoleShell.tsx +35 -30
- package/src/workflow-builder/Page.test.tsx +103 -2
- package/src/workflow-builder/Page.tsx +34 -8
- package/src/workflow-builder/examples/SaveGraph.tsx +17 -0
- package/src/workflow-builder/template.meta.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gntik-ai/templates",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "gntik-ui page templates: complete product pages (auth, dashboard, resources, settings, system) on the kit layouts and blocks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"react-dom": "^19.0.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@gntik-ai/blocks": "0.
|
|
39
|
-
"@gntik-ai/charts": "0.
|
|
40
|
-
"@gntik-ai/chat": "0.
|
|
38
|
+
"@gntik-ai/blocks": "0.10.0",
|
|
39
|
+
"@gntik-ai/charts": "0.10.0",
|
|
40
|
+
"@gntik-ai/chat": "0.10.0",
|
|
41
41
|
"@gntik-ai/editor": "0.5.0",
|
|
42
42
|
"@gntik-ai/flow": "0.5.0",
|
|
43
43
|
"@gntik-ai/icons": "0.5.0",
|
|
44
44
|
"@gntik-ai/tokens": "0.2.1",
|
|
45
|
-
"@gntik-ai/ui": "0.
|
|
45
|
+
"@gntik-ai/ui": "0.10.0",
|
|
46
46
|
"lucide-react": "^1.48.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -57,7 +57,7 @@ export { meta as resetPasswordTemplateMeta } from './reset-password/template.met
|
|
|
57
57
|
export type { ResetPasswordPageProps } from './reset-password/Page';
|
|
58
58
|
export { default as ResourceDetailPage } from './resource-detail/Page';
|
|
59
59
|
export { meta as resourceDetailTemplateMeta } from './resource-detail/template.meta';
|
|
60
|
-
export type { ResourceDetailProps, ResourceDetailTab } from './resource-detail/Page';
|
|
60
|
+
export type { ResourceDetailProps, ResourceDetailTab, ResourceDetailLabels } from './resource-detail/Page';
|
|
61
61
|
export type { ResourceSettings } from './resource-detail/data';
|
|
62
62
|
export { default as ResourceFormPage } from './resource-form/Page';
|
|
63
63
|
export { meta as resourceFormTemplateMeta } from './resource-form/template.meta';
|
|
@@ -2,7 +2,45 @@ import { render, screen, within } from '@testing-library/react';
|
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import { expectNoAxeViolations } from '../test/a11y';
|
|
4
4
|
import KeyboardShortcutsPage, { KeyboardShortcutsDialog } from './Page';
|
|
5
|
-
import { filterShortcuts, shortcutGroups } from './data';
|
|
5
|
+
import { filterShortcuts, shortcutGroups, type ShortcutGroup } from './data';
|
|
6
|
+
import { ShortcutSheet } from './ShortcutSheet';
|
|
7
|
+
|
|
8
|
+
describe('platform shortcut hints', () => {
|
|
9
|
+
afterEach(() => vi.unstubAllGlobals());
|
|
10
|
+
|
|
11
|
+
it('shows Ctrl on Linux and finds the command palette by ctrl k', async () => {
|
|
12
|
+
vi.stubGlobal('navigator', { platform: 'Linux x86_64' });
|
|
13
|
+
render(<ShortcutSheet groups={shortcutGroups} />);
|
|
14
|
+
const row = screen.getByText('Open the command palette').closest('li');
|
|
15
|
+
expect(row).not.toBeNull();
|
|
16
|
+
expect(within(row!).getByText('Ctrl')).toBeInTheDocument();
|
|
17
|
+
await userEvent.type(screen.getByRole('searchbox'), 'ctrl k');
|
|
18
|
+
expect(screen.getByText('Open the command palette')).toBeInTheDocument();
|
|
19
|
+
expect(screen.getByText('1 shortcut')).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('matches platform modifier spellings without changing legacy callers', () => {
|
|
23
|
+
const ids = (groups: ShortcutGroup[]) => groups.flatMap((group) => group.shortcuts.map((shortcut) => shortcut.id));
|
|
24
|
+
expect(ids(filterShortcuts(shortcutGroups, 'CtRl k', 'en', 'linux'))).toEqual(['palette']);
|
|
25
|
+
expect(ids(filterShortcuts(shortcutGroups, '⌘ k', 'en', 'macos'))).toEqual(['palette']);
|
|
26
|
+
expect(ids(filterShortcuts(shortcutGroups, '⌘ k', 'en'))).toEqual(['palette']);
|
|
27
|
+
const legacy: ShortcutGroup[] = [
|
|
28
|
+
{
|
|
29
|
+
id: 'legacy',
|
|
30
|
+
title: 'Legacy',
|
|
31
|
+
shortcuts: [
|
|
32
|
+
{ id: 'literal', description: 'Explicit shortcut', keys: ['⌘', 'K'] },
|
|
33
|
+
{ id: 'sequence', description: 'Sequence', keys: ['G'], then: ['P'] },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
expect(ids(filterShortcuts(legacy, '⌘ k', 'es'))).toEqual(['literal']);
|
|
38
|
+
expect(filterShortcuts(legacy, 'ctrl k', 'es')).toEqual([]);
|
|
39
|
+
expect(ids(filterShortcuts(legacy, 'g p'))).toEqual(['sequence']);
|
|
40
|
+
expect(filterShortcuts(legacy, 'legacy')).toEqual(legacy);
|
|
41
|
+
expect(filterShortcuts(legacy, ' ')).toEqual(legacy);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
6
44
|
|
|
7
45
|
describe('KeyboardShortcutsPage', () => {
|
|
8
46
|
it('renders the console page with every group', { timeout: 15000 }, async () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useI18n } from '@gntik-ai/ui';
|
|
1
|
+
import { useI18n, useOs } from '@gntik-ai/ui';
|
|
2
2
|
import { Keyboard, Search } from '@gntik-ai/icons';
|
|
3
3
|
import { EmptyState, Grid, Heading, Input, KbdCombo, Stack, Text } from '@gntik-ai/ui';
|
|
4
4
|
import { useId, useState } from 'react';
|
|
@@ -16,9 +16,10 @@ export interface ShortcutSheetProps {
|
|
|
16
16
|
/** Searchable shortcut sheet shared by the page and the dialog. */
|
|
17
17
|
export function ShortcutSheet({ groups, headingLevel = 2, columns = 2, autoFocus }: ShortcutSheetProps) {
|
|
18
18
|
const { locale } = useI18n();
|
|
19
|
+
const os = useOs();
|
|
19
20
|
const [query, setQuery] = useState('');
|
|
20
21
|
const countId = useId();
|
|
21
|
-
const visible = filterShortcuts(groups, query, locale);
|
|
22
|
+
const visible = filterShortcuts(groups, query, locale, os);
|
|
22
23
|
const total = visible.reduce((n, g) => n + g.shortcuts.length, 0);
|
|
23
24
|
return (
|
|
24
25
|
<Stack gap={5}>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { matchText } from '@gntik-ai/ui';
|
|
2
|
-
import type { BreadcrumbItem } from '@gntik-ai/ui';
|
|
2
|
+
import type { BreadcrumbItem, Os } from '@gntik-ai/ui';
|
|
3
3
|
|
|
4
4
|
export interface Shortcut {
|
|
5
5
|
id: string;
|
|
6
6
|
/** What the shortcut does. */
|
|
7
7
|
description: string;
|
|
8
|
-
/** Keys pressed together, e.g. ['
|
|
8
|
+
/** Keys pressed together, e.g. ['mod', 'K']. */
|
|
9
9
|
keys: string[];
|
|
10
10
|
/** A second combination that does the same (e.g. a sequence "G then P"). */
|
|
11
11
|
then?: string[];
|
|
@@ -28,10 +28,10 @@ export const shortcutGroups: ShortcutGroup[] = [
|
|
|
28
28
|
id: 'general',
|
|
29
29
|
title: 'General',
|
|
30
30
|
shortcuts: [
|
|
31
|
-
{ id: 'palette', description: 'Open the command palette', keys: ['
|
|
31
|
+
{ id: 'palette', description: 'Open the command palette', keys: ['mod', 'K'] },
|
|
32
32
|
{ id: 'help', description: 'Show keyboard shortcuts', keys: ['?'] },
|
|
33
|
-
{ id: 'sidebar', description: 'Collapse or expand the sidebar', keys: ['
|
|
34
|
-
{ id: 'theme', description: 'Switch theme', keys: ['
|
|
33
|
+
{ id: 'sidebar', description: 'Collapse or expand the sidebar', keys: ['mod', 'B'] },
|
|
34
|
+
{ id: 'theme', description: 'Switch theme', keys: ['mod', '⇧', 'L'] },
|
|
35
35
|
],
|
|
36
36
|
},
|
|
37
37
|
{
|
|
@@ -59,26 +59,40 @@ export const shortcutGroups: ShortcutGroup[] = [
|
|
|
59
59
|
id: 'editing',
|
|
60
60
|
title: 'Editing',
|
|
61
61
|
shortcuts: [
|
|
62
|
-
{ id: 'save', description: 'Save changes', keys: ['
|
|
63
|
-
{ id: 'submit', description: 'Submit a form or comment', keys: ['
|
|
62
|
+
{ id: 'save', description: 'Save changes', keys: ['mod', 'S'] },
|
|
63
|
+
{ id: 'submit', description: 'Submit a form or comment', keys: ['mod', '↵'] },
|
|
64
64
|
{ id: 'cancel', description: 'Cancel and close', keys: ['Esc'] },
|
|
65
|
-
{ id: 'undo', description: 'Undo', keys: ['
|
|
65
|
+
{ id: 'undo', description: 'Undo', keys: ['mod', 'Z'] },
|
|
66
66
|
],
|
|
67
67
|
},
|
|
68
68
|
];
|
|
69
69
|
|
|
70
|
-
/** Groups
|
|
71
|
-
export function filterShortcuts(
|
|
70
|
+
/** Groups matching descriptions, titles or keys; mod accepts both Command and Ctrl spellings. */
|
|
71
|
+
export function filterShortcuts(
|
|
72
|
+
groups: readonly ShortcutGroup[],
|
|
73
|
+
query: string,
|
|
74
|
+
locale?: string,
|
|
75
|
+
os: Os = 'undetermined',
|
|
76
|
+
): ShortcutGroup[] {
|
|
72
77
|
const q = query.trim().toLowerCase();
|
|
73
78
|
if (!q) return [...groups];
|
|
79
|
+
const modifier = os === 'macos' || os === 'ios' ? '⌘' : 'Ctrl';
|
|
80
|
+
const alias = modifier === '⌘' ? 'Ctrl' : '⌘';
|
|
81
|
+
const matchesKeys = (shortcut: Shortcut) => {
|
|
82
|
+
const keys = [...shortcut.keys, ...(shortcut.then ?? [])];
|
|
83
|
+
return ['mod', modifier, alias].some(
|
|
84
|
+
(mod) =>
|
|
85
|
+
keys
|
|
86
|
+
.map((key) => (key === 'mod' ? mod : key))
|
|
87
|
+
.join(' ')
|
|
88
|
+
.toLowerCase() === q,
|
|
89
|
+
);
|
|
90
|
+
};
|
|
74
91
|
return groups
|
|
75
92
|
.map((g) => ({
|
|
76
93
|
...g,
|
|
77
94
|
shortcuts: g.shortcuts.filter(
|
|
78
|
-
(s) =>
|
|
79
|
-
matchText(s.description, query, { locale }) ||
|
|
80
|
-
matchText(g.title, query, { locale }) ||
|
|
81
|
-
[...s.keys, ...(s.then ?? [])].join(' ').toLowerCase() === q,
|
|
95
|
+
(s) => matchText(s.description, query, { locale }) || matchText(g.title, query, { locale }) || matchesKeys(s),
|
|
82
96
|
),
|
|
83
97
|
}))
|
|
84
98
|
.filter((g) => g.shortcuts.length > 0);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { act, render, screen } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { I18nProvider, es } from '@gntik-ai/ui';
|
|
4
|
+
import ResourceDetailPage, { type ResourceDetailLabels } from './Page';
|
|
5
|
+
import { detailResource, detailMeta, detailItems } from './data';
|
|
6
|
+
import { expectNoAxeViolations } from '../test/a11y';
|
|
7
|
+
|
|
8
|
+
it(
|
|
9
|
+
'localizes only resource tabs and their tablist, preserves fixtures, and remains keyboard accessible',
|
|
10
|
+
{ timeout: 15000 },
|
|
11
|
+
async () => {
|
|
12
|
+
const user = userEvent.setup();
|
|
13
|
+
const { container } = render(
|
|
14
|
+
<I18nProvider locale="es">
|
|
15
|
+
<ResourceDetailPage />
|
|
16
|
+
</I18nProvider>,
|
|
17
|
+
);
|
|
18
|
+
expect(screen.getByRole('tablist', { name: es['resourceDetail.sections'] })).toBeVisible();
|
|
19
|
+
for (const key of ['overview', 'activity', 'settings'] as const)
|
|
20
|
+
expect(screen.getByRole('tab', { name: es[`resourceDetail.${key}`] })).toBeVisible();
|
|
21
|
+
const overview = screen.getByRole('tab', { name: 'Resumen' });
|
|
22
|
+
expect(overview).toHaveAttribute('aria-selected', 'true');
|
|
23
|
+
act(() => overview.focus());
|
|
24
|
+
await user.keyboard('{ArrowRight}{Enter}');
|
|
25
|
+
expect(screen.getByRole('tab', { name: 'Actividad' })).toHaveAttribute('aria-selected', 'true');
|
|
26
|
+
await user.click(overview);
|
|
27
|
+
expect(screen.getByRole('heading', { name: detailResource.name })).toBeVisible();
|
|
28
|
+
expect(screen.getByText(detailResource.description)).toBeVisible();
|
|
29
|
+
for (const item of detailMeta) expect(screen.getAllByText(String(item.value))[0]).toBeVisible();
|
|
30
|
+
for (const item of detailItems) expect(screen.getAllByText(item.label)[0]).toBeVisible();
|
|
31
|
+
expect(screen.getByRole('heading', { name: 'Configuration' })).toBeVisible();
|
|
32
|
+
expect(screen.getAllByRole('button', { name: 'View logs' })[0]).toBeVisible();
|
|
33
|
+
expect(screen.getAllByRole('link', { name: 'Deployments' })[0]).toHaveAttribute('href', '/deployments');
|
|
34
|
+
expect(container.textContent).not.toContain('{');
|
|
35
|
+
await expectNoAxeViolations(container);
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
it('keeps the English defaults and renders every tab override with catalog fallback in readOnly mode', () => {
|
|
40
|
+
const { rerender } = render(<ResourceDetailPage />);
|
|
41
|
+
expect(screen.getByRole('tablist', { name: 'Resource sections' })).toBeVisible();
|
|
42
|
+
for (const name of ['Overview', 'Activity', 'Settings']) expect(screen.getByRole('tab', { name })).toBeVisible();
|
|
43
|
+
const labels: ResourceDetailLabels = {
|
|
44
|
+
overview: 'Overview override',
|
|
45
|
+
activity: 'Activity override',
|
|
46
|
+
settings: 'Settings override',
|
|
47
|
+
sections: 'Sections override',
|
|
48
|
+
};
|
|
49
|
+
const view = (overrides: Partial<ResourceDetailLabels>, readOnly = false) => (
|
|
50
|
+
<I18nProvider locale="es">
|
|
51
|
+
<ResourceDetailPage labels={overrides} readOnly={readOnly} />
|
|
52
|
+
</I18nProvider>
|
|
53
|
+
);
|
|
54
|
+
rerender(view(labels));
|
|
55
|
+
expect(screen.getByRole('tablist', { name: labels.sections })).toBeVisible();
|
|
56
|
+
for (const name of [labels.overview, labels.activity, labels.settings])
|
|
57
|
+
expect(screen.getByRole('tab', { name })).toBeVisible();
|
|
58
|
+
rerender(view({ overview: 'Only overview' }));
|
|
59
|
+
expect(screen.getByRole('tablist', { name: es['resourceDetail.sections'] })).toBeVisible();
|
|
60
|
+
expect(screen.getByRole('tab', { name: 'Only overview' })).toBeVisible();
|
|
61
|
+
expect(screen.getByRole('tab', { name: es['resourceDetail.activity'] })).toBeVisible();
|
|
62
|
+
expect(screen.getByRole('tab', { name: es['resourceDetail.settings'] })).toBeVisible();
|
|
63
|
+
rerender(view({}, true));
|
|
64
|
+
expect(screen.getAllByRole('tab')).toHaveLength(1);
|
|
65
|
+
expect(screen.getByRole('tab', { name: es['resourceDetail.overview'] })).toHaveAttribute('aria-selected', 'true');
|
|
66
|
+
expect(screen.getByRole('tablist', { name: es['resourceDetail.sections'] })).toBeVisible();
|
|
67
|
+
});
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
TabsPanel,
|
|
29
29
|
TabsTab,
|
|
30
30
|
Textarea,
|
|
31
|
+
useI18n,
|
|
31
32
|
type BreadcrumbItem,
|
|
32
33
|
} from '@gntik-ai/ui';
|
|
33
34
|
import { useState } from 'react';
|
|
@@ -45,7 +46,16 @@ import {
|
|
|
45
46
|
|
|
46
47
|
export type ResourceDetailTab = 'overview' | 'activity' | 'settings';
|
|
47
48
|
|
|
49
|
+
export interface ResourceDetailLabels {
|
|
50
|
+
overview: string;
|
|
51
|
+
activity: string;
|
|
52
|
+
settings: string;
|
|
53
|
+
sections: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
export interface ResourceDetailProps {
|
|
57
|
+
/** Per-key copy overrides; explicit props take precedence, then labels, then the active catalog. */
|
|
58
|
+
labels?: Partial<ResourceDetailLabels>;
|
|
49
59
|
/**
|
|
50
60
|
* Optional boolean, default false. Shows only the keyboard-accessible Overview tab in
|
|
51
61
|
* the "Resource sections" tablist, using explicit resource props without sample defaults.
|
|
@@ -85,14 +95,11 @@ export interface ResourceDetailProps {
|
|
|
85
95
|
shell: Omit<ConsoleShellProps, 'children'>;
|
|
86
96
|
}
|
|
87
97
|
|
|
88
|
-
const TABS:
|
|
89
|
-
{ value: 'overview', label: 'Overview' },
|
|
90
|
-
{ value: 'activity', label: 'Activity' },
|
|
91
|
-
{ value: 'settings', label: 'Settings' },
|
|
92
|
-
];
|
|
98
|
+
const TABS: ResourceDetailTab[] = ['overview', 'activity', 'settings'];
|
|
93
99
|
|
|
94
100
|
/** Resource detail: editable tabs by default, or explicit-only Overview when readOnly is true. */
|
|
95
101
|
export default function ResourceDetailPage(props: Partial<ResourceDetailProps>) {
|
|
102
|
+
const { t } = useI18n();
|
|
96
103
|
const readOnly = props.readOnly ?? false;
|
|
97
104
|
const {
|
|
98
105
|
name = readOnly ? '' : detailResource.name,
|
|
@@ -114,6 +121,7 @@ export default function ResourceDetailPage(props: Partial<ResourceDetailProps>)
|
|
|
114
121
|
onSaveSettings,
|
|
115
122
|
onDangerAction,
|
|
116
123
|
shell,
|
|
124
|
+
labels,
|
|
117
125
|
} = props;
|
|
118
126
|
const meta = metaProp ?? (readOnly ? [] : detailMeta);
|
|
119
127
|
const actions: PageChromeAction[] =
|
|
@@ -130,7 +138,10 @@ export default function ResourceDetailPage(props: Partial<ResourceDetailProps>)
|
|
|
130
138
|
const [settings, setSettings] = useState<ResourceSettings>({ name, description });
|
|
131
139
|
const [saved, setSaved] = useState<ResourceSettings>({ name, description });
|
|
132
140
|
const dirty = settings.name !== saved.name || settings.description !== saved.description;
|
|
133
|
-
const tabs = readOnly ? TABS.filter((
|
|
141
|
+
const tabs = (readOnly ? TABS.filter((value) => value === 'overview') : TABS).map((value) => ({
|
|
142
|
+
value,
|
|
143
|
+
label: labels?.[value] ?? t(`resourceDetail.${value}`),
|
|
144
|
+
}));
|
|
134
145
|
|
|
135
146
|
return (
|
|
136
147
|
<ConsoleShell currentHref="/deployments" breadcrumbs={breadcrumbs} {...shell}>
|
|
@@ -156,7 +167,7 @@ export default function ResourceDetailPage(props: Partial<ResourceDetailProps>)
|
|
|
156
167
|
onTabChange?.(next);
|
|
157
168
|
}}
|
|
158
169
|
>
|
|
159
|
-
<TabsList aria-label=
|
|
170
|
+
<TabsList aria-label={labels?.sections ?? t('resourceDetail.sections')}>
|
|
160
171
|
{tabs.map((t) => (
|
|
161
172
|
<TabsTab key={t.value} value={t.value}>
|
|
162
173
|
{t.label}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { I18nProvider } from '@gntik-ai/ui';
|
|
2
|
+
import ResourceDetailPage from '../Page';
|
|
3
|
+
|
|
4
|
+
/** Each tab label and the tablist name can be overridden independently. */
|
|
5
|
+
export default function Labels() {
|
|
6
|
+
return (
|
|
7
|
+
<I18nProvider locale="es">
|
|
8
|
+
<ResourceDetailPage
|
|
9
|
+
labels={{
|
|
10
|
+
overview: 'Resource overview',
|
|
11
|
+
activity: 'Resource activity',
|
|
12
|
+
settings: 'Resource settings',
|
|
13
|
+
sections: 'Resource navigation',
|
|
14
|
+
}}
|
|
15
|
+
/>
|
|
16
|
+
</I18nProvider>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { I18nProvider } from '@gntik-ai/ui';
|
|
2
|
+
import ResourceDetailPage from '../Page';
|
|
3
|
+
|
|
4
|
+
/** Only tab chrome is translated; imported resource data and section content stay unchanged. */
|
|
5
|
+
export default function Spanish() {
|
|
6
|
+
return (
|
|
7
|
+
<I18nProvider locale="es">
|
|
8
|
+
<ResourceDetailPage />
|
|
9
|
+
</I18nProvider>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -6,7 +6,7 @@ export const meta: TemplateMeta = {
|
|
|
6
6
|
priority: 'P1',
|
|
7
7
|
status: 'beta',
|
|
8
8
|
description:
|
|
9
|
-
'Resource detail with editable Overview, Activity and Settings tabs by default; optional readOnly (boolean, default false) uses only explicit header and detail props and one Overview tab, with optional history (readonly StatusEvent[], default undefined): omitted hides the history card, [] shows the named empty state, and events render exactly as supplied without invented actors. Read-only ignores activity, events, dangerActions, defaultTab, onSaveSettings and onDangerAction; explicit actions and onEditDetails are honored. The Resource sections tablist retains Tab, arrow, Home/End and Enter/Space keyboard navigation; history is a named group containing the Status history list or empty-state region. Uses token styling in dark (default), light and high_contrast. Examples: runtime-with-transition maps state and lastTransitionAt to one actor-free event, runtime-without-transition maps null to [], and realtime-configuration and postgres-table-detail omit history. ConsoleShell retains sample frame defaults unless explicit shell props are supplied, as in the examples.',
|
|
9
|
+
'Resource detail with editable Overview, Activity and Settings tabs by default; optional readOnly (boolean, default false) uses only explicit header and detail props and one Overview tab, with optional history (readonly StatusEvent[], default undefined): omitted hides the history card, [] shows the named empty state, and events render exactly as supplied without invented actors. Read-only ignores activity, events, dangerActions, defaultTab, onSaveSettings and onDangerAction; explicit actions and onEditDetails are honored. The Resource sections tablist retains Tab, arrow, Home/End and Enter/Space keyboard navigation; history is a named group containing the Status history list or empty-state region. Uses token styling in dark (default), light and high_contrast. Examples: runtime-with-transition maps state and lastTransitionAt to one actor-free event, runtime-without-transition maps null to [], and realtime-configuration and postgres-table-detail omit history. Optional labels?: Partial<ResourceDetailLabels> overrides overview, activity, settings and sections (all strings). Unset keys use I18nProvider (en/es); no provider keeps English. Only the tabs and tablist are localized by this template; fixtures and other section text are unchanged. readOnly keeps only the localized overview tab. See the Catalog labels live preview. ConsoleShell retains sample frame defaults unless explicit shell props are supplied, as in the examples.',
|
|
10
10
|
layout: 'SidebarLayout + Page',
|
|
11
11
|
blocks: [
|
|
12
12
|
'app-sidebar',
|