@gntik-ai/blocks 0.7.0 → 0.9.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 +34 -7
- package/dist/index.js +283 -109
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/builders/LogViewer/LogViewer.test.tsx +11 -0
- package/src/builders/LogViewer/LogViewer.tsx +28 -22
- package/src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.additional.test.tsx +467 -0
- package/src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.tsx +175 -106
- package/src/forms/CredentialDisclosureDialog/IssueOnClick.test.tsx +56 -0
- package/src/forms/CredentialDisclosureDialog/SecretCopy.tsx +83 -0
- package/src/forms/CredentialDisclosureDialog/block.meta.ts +1 -1
- package/src/forms/CredentialDisclosureDialog/examples/Inline.tsx +19 -0
- package/src/forms/CredentialDisclosureDialog/examples/IssueOnClick.tsx +87 -0
- package/src/forms/CredentialDisclosureDialog/examples/MultiSecret.tsx +29 -0
- package/src/forms/CredentialDisclosureDialog/focus.ts +18 -0
- package/src/forms/CredentialDisclosureDialog/types.ts +5 -0
- package/src/page-chrome/PageHeader/PageHeader.test.tsx +18 -0
- package/src/page-chrome/PageHeader/PageHeader.tsx +3 -1
- package/src/page-chrome/PageHeader/block.meta.ts +1 -1
- package/src/page-chrome/PageHeader/examples/Spanish.tsx +11 -0
- package/src/tables/DataTable/DataTable.labels.test.tsx +2 -0
- package/src/tables/DataTable/DataTable.locale.test.tsx +153 -0
- package/src/tables/DataTable/DataTable.server-pagination.test.tsx +1 -1
- package/src/tables/DataTable/DataTable.test.tsx +16 -0
- package/src/tables/DataTable/block.meta.ts +2 -0
- package/src/tables/DataTable/data-table-row.tsx +18 -2
- package/src/tables/DataTable/data-table-utils.ts +18 -4
- package/src/tables/DataTable/labels.ts +2 -0
- package/src/test/CredentialDisclosureDialog.compatibility.tsx +30 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import Spanish from './examples/Spanish';
|
|
2
|
+
import { I18nProvider } from '@gntik-ai/ui';
|
|
1
3
|
import { render, screen } from '@testing-library/react';
|
|
2
4
|
import userEvent from '@testing-library/user-event';
|
|
3
5
|
import { createRef } from 'react';
|
|
@@ -34,6 +36,15 @@ describe('PageHeader', () => {
|
|
|
34
36
|
expect(screen.getByRole('heading', { level: 1 })).not.toHaveAttribute('tabindex');
|
|
35
37
|
});
|
|
36
38
|
|
|
39
|
+
it('names the tablist from the active Spanish catalog', () => {
|
|
40
|
+
render(
|
|
41
|
+
<I18nProvider locale="es">
|
|
42
|
+
<PageHeader />
|
|
43
|
+
</I18nProvider>,
|
|
44
|
+
);
|
|
45
|
+
expect(screen.getByRole('tablist', { name: 'Secciones de la página' })).toBeInTheDocument();
|
|
46
|
+
});
|
|
47
|
+
|
|
37
48
|
it('renders the default header', async () => {
|
|
38
49
|
const { container } = render(<PageHeader />);
|
|
39
50
|
expect(screen.getByRole('heading', { level: 1, name: 'Deployments' })).toBeInTheDocument();
|
|
@@ -66,3 +77,10 @@ describe('PageHeader', () => {
|
|
|
66
77
|
expect(onTabChange).toHaveBeenCalledWith('domains');
|
|
67
78
|
});
|
|
68
79
|
});
|
|
80
|
+
|
|
81
|
+
it.each(['dark', 'light', 'high_contrast'])('Spanish PageHeader example is axe clean (%s)', async (theme) => {
|
|
82
|
+
document.documentElement.className = theme === 'light' ? '' : theme;
|
|
83
|
+
const { container } = render(<Spanish />);
|
|
84
|
+
expect(screen.getByRole('tablist', { name: 'Secciones de la página' })).toBeInTheDocument();
|
|
85
|
+
await expectNoAxeViolations(container);
|
|
86
|
+
});
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
TabsList,
|
|
8
8
|
TabsTab,
|
|
9
9
|
cn,
|
|
10
|
+
useI18n,
|
|
10
11
|
type BreadcrumbItem,
|
|
11
12
|
type StatusDefinition,
|
|
12
13
|
} from '@gntik-ai/ui';
|
|
@@ -74,6 +75,7 @@ export function PageHeader({
|
|
|
74
75
|
onTabChange,
|
|
75
76
|
className,
|
|
76
77
|
}: PageHeaderProps) {
|
|
78
|
+
const { t } = useI18n();
|
|
77
79
|
const explicitPrimary = actions.findIndex((a) => a.variant === 'primary');
|
|
78
80
|
const primaryIndex = explicitPrimary >= 0 ? explicitPrimary : actions.length - 1;
|
|
79
81
|
const primary = actions[primaryIndex];
|
|
@@ -138,7 +140,7 @@ export function PageHeader({
|
|
|
138
140
|
onValueChange={(value) => onTabChange?.(String(value))}
|
|
139
141
|
className="mt-5"
|
|
140
142
|
>
|
|
141
|
-
<TabsList aria-label=
|
|
143
|
+
<TabsList aria-label={t('pageHeader.sections')} className="overflow-x-auto">
|
|
142
144
|
{tabs.map((t) => (
|
|
143
145
|
<TabsTab key={t.value} value={t.value} count={t.count}>
|
|
144
146
|
{t.label}
|
|
@@ -5,6 +5,6 @@ export const meta: BlockMeta = {
|
|
|
5
5
|
family: 'page-chrome',
|
|
6
6
|
status: 'beta',
|
|
7
7
|
description:
|
|
8
|
-
'Screen header with breadcrumb, title and status, description, meta row, actions that fold into a MoreMenu on small screens, and optional navigation tabs. titleRef targets the h1 for titleRef.current.focus(), adding tabIndex=-1 only when supplied so the title stays outside the Tab order. In a StatusLayout heading slot use as="div" and disable breadcrumb, meta, tabs, actions, status and description fixtures; the slot supplies the page’s only h1.',
|
|
8
|
+
'Screen header with breadcrumb, title and status, description, meta row, actions that fold into a MoreMenu on small screens, and optional navigation tabs. titleRef targets the h1 for titleRef.current.focus(), adding tabIndex=-1 only when supplied so the title stays outside the Tab order. In a StatusLayout heading slot use as="div" and disable breadcrumb, meta, tabs, actions, status and description fixtures; the slot supplies the page’s only h1. The tablist name follows I18nProvider (en/es); the Spanish preview keeps caller content unchanged.',
|
|
9
9
|
uses: ['Breadcrumbs', 'Button', 'MoreMenu', 'StatusTag', 'Tabs'],
|
|
10
10
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { I18nProvider } from '@gntik-ai/ui';
|
|
2
|
+
import { PageHeader } from '../PageHeader';
|
|
3
|
+
|
|
4
|
+
/** Caller-supplied content is unchanged; the tablist name follows the active catalog. */
|
|
5
|
+
export default function Spanish() {
|
|
6
|
+
return (
|
|
7
|
+
<I18nProvider locale="es">
|
|
8
|
+
<PageHeader />
|
|
9
|
+
</I18nProvider>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -233,6 +233,8 @@ it('replaces every pin option and the single-selection heading without translati
|
|
|
233
233
|
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
|
234
234
|
const full: DataTableLabels = {
|
|
235
235
|
...labels,
|
|
236
|
+
booleanTrue: 'Sí',
|
|
237
|
+
booleanFalse: 'No',
|
|
236
238
|
selectGroup: (group) => `Grupo ${group}`,
|
|
237
239
|
groupRows: (count) => `${count} filas`,
|
|
238
240
|
noGroup: 'Sin grupo',
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { I18nProvider } from '@gntik-ai/ui';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { expectNoAxeViolations } from '../../test/a11y';
|
|
5
|
+
import { DataTable } from './DataTable';
|
|
6
|
+
import type { CellValue, DataTableColumn } from './data-table-utils';
|
|
7
|
+
|
|
8
|
+
type Row = { id: string; value: CellValue };
|
|
9
|
+
const columns: DataTableColumn<Row>[] = [
|
|
10
|
+
{ id: 'value', header: 'Value', accessor: (row) => row.value, sortable: true },
|
|
11
|
+
];
|
|
12
|
+
const rowsOf = (...values: CellValue[]): Row[] => values.map((value, i) => ({ id: String(i), value }));
|
|
13
|
+
const cellTexts = () => screen.getAllByRole('cell').map((cell) => cell.textContent);
|
|
14
|
+
|
|
15
|
+
describe('DataTable default cell locale', () => {
|
|
16
|
+
it('reads boolean labels from the Spanish catalog', () => {
|
|
17
|
+
render(
|
|
18
|
+
<I18nProvider locale="es">
|
|
19
|
+
<DataTable rows={rowsOf(true, false)} columns={columns} selectable={false} paginated={false} />
|
|
20
|
+
</I18nProvider>,
|
|
21
|
+
);
|
|
22
|
+
expect(cellTexts()).toEqual(['Sí', 'No']);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it.each(['es', 'en'])('prefers per-instance boolean labels under %s', (locale) => {
|
|
26
|
+
render(
|
|
27
|
+
<I18nProvider locale={locale}>
|
|
28
|
+
<DataTable
|
|
29
|
+
rows={rowsOf(true, false)}
|
|
30
|
+
columns={columns}
|
|
31
|
+
selectable={false}
|
|
32
|
+
paginated={false}
|
|
33
|
+
labels={{ booleanTrue: 'On', booleanFalse: 'Off' }}
|
|
34
|
+
/>
|
|
35
|
+
</I18nProvider>,
|
|
36
|
+
);
|
|
37
|
+
expect(cellTexts()).toEqual(['On', 'Off']);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it.each([
|
|
41
|
+
['es', '17/10/2026'],
|
|
42
|
+
['en-US', '10/17/2026'],
|
|
43
|
+
])('formats valid dates and handles invalid dates under %s', (locale, expected) => {
|
|
44
|
+
render(
|
|
45
|
+
<I18nProvider locale={locale}>
|
|
46
|
+
<DataTable
|
|
47
|
+
rows={rowsOf(new Date(2026, 9, 17), new Date('x'))}
|
|
48
|
+
columns={columns}
|
|
49
|
+
selectable={false}
|
|
50
|
+
paginated={false}
|
|
51
|
+
/>
|
|
52
|
+
</I18nProvider>,
|
|
53
|
+
);
|
|
54
|
+
expect(cellTexts()).toEqual([expected, '—']);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it.each([
|
|
58
|
+
['es', ['1234,5', '1234567', '0,30000000000000004', 'NaN', 'Infinity', '-Infinity']],
|
|
59
|
+
['en', ['1234.5', '1234567', '0.30000000000000004', 'NaN', 'Infinity', '-Infinity']],
|
|
60
|
+
] as const)('keeps number digits and only localizes the decimal separator under %s', (locale, expected) => {
|
|
61
|
+
render(
|
|
62
|
+
<I18nProvider locale={locale}>
|
|
63
|
+
<DataTable
|
|
64
|
+
rows={rowsOf(1234.5, 1234567, 0.30000000000000004, NaN, Infinity, -Infinity)}
|
|
65
|
+
columns={columns}
|
|
66
|
+
selectable={false}
|
|
67
|
+
paginated={false}
|
|
68
|
+
/>
|
|
69
|
+
</I18nProvider>,
|
|
70
|
+
);
|
|
71
|
+
expect(cellTexts()).toEqual(expected);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('keeps English boolean labels under an English provider', () => {
|
|
75
|
+
render(
|
|
76
|
+
<I18nProvider locale="en">
|
|
77
|
+
<DataTable rows={rowsOf(true, false)} columns={columns} selectable={false} paginated={false} />
|
|
78
|
+
</I18nProvider>,
|
|
79
|
+
);
|
|
80
|
+
expect(cellTexts()).toEqual(['Yes', 'No']);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('uses provider catalog overrides with individual label fallbacks', () => {
|
|
84
|
+
render(
|
|
85
|
+
<I18nProvider locale="es" messages={{ 'common.yes': 'Affirmative', 'common.no': 'Negative' }}>
|
|
86
|
+
<DataTable
|
|
87
|
+
rows={rowsOf(true, false)}
|
|
88
|
+
columns={columns}
|
|
89
|
+
selectable={false}
|
|
90
|
+
paginated={false}
|
|
91
|
+
labels={{ booleanTrue: 'On' }}
|
|
92
|
+
/>
|
|
93
|
+
</I18nProvider>,
|
|
94
|
+
);
|
|
95
|
+
expect(cellTexts()).toEqual(['On', 'Negative']);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('keeps runtime defaults outside a provider', () => {
|
|
99
|
+
const date = new Date(2026, 9, 17);
|
|
100
|
+
render(
|
|
101
|
+
<DataTable
|
|
102
|
+
rows={rowsOf(true, false, date, 1234.5, 1234567, 0.30000000000000004)}
|
|
103
|
+
columns={columns}
|
|
104
|
+
selectable={false}
|
|
105
|
+
paginated={false}
|
|
106
|
+
/>,
|
|
107
|
+
);
|
|
108
|
+
expect(cellTexts()).toEqual(['Yes', 'No', date.toLocaleDateString(), '1234.5', '1234567', '0.30000000000000004']);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('sorts raw numeric accessor values while showing Spanish decimals', async () => {
|
|
112
|
+
const user = userEvent.setup();
|
|
113
|
+
render(
|
|
114
|
+
<I18nProvider locale="es">
|
|
115
|
+
<DataTable rows={rowsOf(10.1, 2.2, 2.11)} columns={columns} selectable={false} paginated={false} />
|
|
116
|
+
</I18nProvider>,
|
|
117
|
+
);
|
|
118
|
+
await user.click(screen.getByRole('button', { name: 'Value' }));
|
|
119
|
+
expect(cellTexts()).toEqual(['2,11', '2,2', '10,1']);
|
|
120
|
+
await user.click(screen.getByRole('button', { name: 'Value' }));
|
|
121
|
+
expect(cellTexts()).toEqual(['10,1', '2,2', '2,11']);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('leaves custom cell renderers in control', () => {
|
|
125
|
+
render(
|
|
126
|
+
<I18nProvider locale="es">
|
|
127
|
+
<DataTable
|
|
128
|
+
rows={rowsOf(true, new Date('x'), 1234.5)}
|
|
129
|
+
columns={[{ ...columns[0]!, cell: () => 'Custom value' }]}
|
|
130
|
+
selectable={false}
|
|
131
|
+
paginated={false}
|
|
132
|
+
/>
|
|
133
|
+
</I18nProvider>,
|
|
134
|
+
);
|
|
135
|
+
expect(cellTexts()).toEqual(['Custom value', 'Custom value', 'Custom value']);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it.each(['dark', 'light', 'high_contrast'])('has no structural axe violations in %s', async (theme) => {
|
|
139
|
+
document.documentElement.className = theme;
|
|
140
|
+
const { container } = render(
|
|
141
|
+
<I18nProvider locale="es">
|
|
142
|
+
<DataTable
|
|
143
|
+
rows={rowsOf(true, false, new Date(2026, 9, 17), new Date('x'), 1234.5)}
|
|
144
|
+
columns={columns}
|
|
145
|
+
caption="Values"
|
|
146
|
+
selectable={false}
|
|
147
|
+
paginated={false}
|
|
148
|
+
/>
|
|
149
|
+
</I18nProvider>,
|
|
150
|
+
);
|
|
151
|
+
await expectNoAxeViolations(container);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
@@ -97,7 +97,7 @@ describe('DataTable controlled server pagination', () => {
|
|
|
97
97
|
const onPageSizeChange = vi.fn();
|
|
98
98
|
render(<DataTable {...server} onPageChange={onPageChange} onPageSizeChange={onPageSizeChange} />);
|
|
99
99
|
await user.click(screen.getByRole('combobox', { name: 'Rows per page' }));
|
|
100
|
-
await user.click(screen.
|
|
100
|
+
await user.click(await screen.findByRole('option', { name: '50' }));
|
|
101
101
|
expect(onPageSizeChange.mock.calls).toEqual([[50]]);
|
|
102
102
|
expect(onPageChange.mock.calls).toEqual([[1]]);
|
|
103
103
|
expect(renderedIds()).toHaveLength(20);
|
|
@@ -5,6 +5,7 @@ import { expectNoAxeViolations } from '../../test/a11y';
|
|
|
5
5
|
import { DataTable } from './DataTable';
|
|
6
6
|
import {
|
|
7
7
|
compareValues,
|
|
8
|
+
formatCell,
|
|
8
9
|
groupRows,
|
|
9
10
|
paginate,
|
|
10
11
|
selectionState,
|
|
@@ -142,6 +143,21 @@ describe('DataTable', () => {
|
|
|
142
143
|
});
|
|
143
144
|
|
|
144
145
|
describe('data-table-utils', () => {
|
|
146
|
+
it('preserves one-argument default cell values', () => {
|
|
147
|
+
expect(formatCell(null)).toBe('—');
|
|
148
|
+
expect(formatCell(undefined)).toBe('—');
|
|
149
|
+
expect(formatCell(true)).toBe('Yes');
|
|
150
|
+
expect(formatCell(false)).toBe('No');
|
|
151
|
+
const date = new Date(2026, 9, 17);
|
|
152
|
+
expect(formatCell(date)).toBe(date.toLocaleDateString());
|
|
153
|
+
expect(formatCell(new Date('x'))).toBe('Invalid Date');
|
|
154
|
+
expect(formatCell(1234.5)).toBe(1234.5);
|
|
155
|
+
expect(formatCell(0.30000000000000004)).toBe(0.30000000000000004);
|
|
156
|
+
expect(formatCell(NaN)).toBeNaN();
|
|
157
|
+
expect(formatCell(Infinity)).toBe(Infinity);
|
|
158
|
+
expect(formatCell('Text')).toBe('Text');
|
|
159
|
+
});
|
|
160
|
+
|
|
145
161
|
type Row = { id: string; n: number | null };
|
|
146
162
|
const cols: DataTableColumn<Row>[] = [{ id: 'n', header: 'N', accessor: (r) => r.n }];
|
|
147
163
|
const rows: Row[] = [
|
|
@@ -12,6 +12,8 @@ export const meta: BlockMeta = {
|
|
|
12
12
|
'Opt-in paginationMode="server" accepts a controlled 1-based page, pageSize and required rowCount (known total), with onPageChange/onPageSizeChange requests. Supplied rows render in order without client sorting or slicing, and headers have no sort affordance. Single selection cannot use server mode. The footer stays visible for empty results, clamps stale pages for display without callbacks, and hides during loading. Page-size changes may also request a page to keep the first visible row on screen. The consumer owns fetching and reconciles stale pages. See examples/server-pagination.tsx for an items/total/page/pageSize envelope consumer that handles both callbacks.',
|
|
13
13
|
|
|
14
14
|
labels: [
|
|
15
|
+
{ key: 'booleanTrue', type: 'string', state: 'Default cell text for a true boolean value' },
|
|
16
|
+
{ key: 'booleanFalse', type: 'string', state: 'Default cell text for a false boolean value' },
|
|
15
17
|
{
|
|
16
18
|
key: 'scrollRegion',
|
|
17
19
|
type: 'string',
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { ChevronRight } from '@gntik-ai/icons';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Checkbox,
|
|
4
|
+
cn,
|
|
5
|
+
MoreMenu,
|
|
6
|
+
TableCell,
|
|
7
|
+
TableHead,
|
|
8
|
+
TableRow,
|
|
9
|
+
type MoreMenuAction,
|
|
10
|
+
useI18n,
|
|
11
|
+
useOptionalI18n,
|
|
12
|
+
} from '@gntik-ai/ui';
|
|
3
13
|
import type { CSSProperties, ReactNode } from 'react';
|
|
4
14
|
import { stickyTint, type TableLayout } from './column-sizing';
|
|
5
15
|
import {
|
|
@@ -68,6 +78,7 @@ export function DataTableRow<T>({
|
|
|
68
78
|
style,
|
|
69
79
|
}: DataTableRowProps<T>) {
|
|
70
80
|
const { t } = useI18n();
|
|
81
|
+
const locale = useOptionalI18n()?.locale;
|
|
71
82
|
const id = getRowId(row);
|
|
72
83
|
const selectionColumn = selectable || singleSelection != null;
|
|
73
84
|
const isSelected = singleSelection ? singleSelection.selectedId === id : selected.has(id);
|
|
@@ -95,7 +106,12 @@ export function DataTableRow<T>({
|
|
|
95
106
|
)}
|
|
96
107
|
{columns.map((c) => {
|
|
97
108
|
const cl = layout?.columns.get(c.id);
|
|
98
|
-
const content = c.cell
|
|
109
|
+
const content = c.cell
|
|
110
|
+
? c.cell(row)
|
|
111
|
+
: formatCell(c.accessor?.(row), {
|
|
112
|
+
locale,
|
|
113
|
+
booleanLabels: [labels?.booleanTrue ?? t('common.yes'), labels?.booleanFalse ?? t('common.no')],
|
|
114
|
+
});
|
|
99
115
|
const editable = onCellEdit && editHintId && isCellEditable(c, row);
|
|
100
116
|
return (
|
|
101
117
|
<TableCell
|
|
@@ -147,11 +147,25 @@ export function toggleOne(id: string, selected: ReadonlySet<string>): Set<string
|
|
|
147
147
|
return next;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
/**
|
|
151
|
-
export
|
|
150
|
+
/** Optional locale and boolean text for default cells. */
|
|
151
|
+
export interface DataTableCellFormatOptions {
|
|
152
|
+
locale?: string;
|
|
153
|
+
booleanLabels?: [trueLabel: string, falseLabel: string];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Default cell text for a raw value. Omitting options preserves the legacy output. */
|
|
157
|
+
export function formatCell(value: CellValue, options?: DataTableCellFormatOptions): ReactNode {
|
|
152
158
|
if (value == null) return '—';
|
|
153
|
-
if (value instanceof Date)
|
|
154
|
-
|
|
159
|
+
if (value instanceof Date) {
|
|
160
|
+
if (!options) return value.toLocaleDateString();
|
|
161
|
+
if (Number.isNaN(value.getTime())) return '—';
|
|
162
|
+
return new Intl.DateTimeFormat(options.locale, { year: 'numeric', month: 'numeric', day: 'numeric' }).format(value);
|
|
163
|
+
}
|
|
164
|
+
if (typeof value === 'boolean') return (options?.booleanLabels ?? ['Yes', 'No'])[value ? 0 : 1];
|
|
165
|
+
if (typeof value === 'number' && Number.isFinite(value) && options?.locale !== undefined) {
|
|
166
|
+
const decimal = new Intl.NumberFormat(options.locale).formatToParts(1.1).find((part) => part.type === 'decimal');
|
|
167
|
+
return String(value).replace('.', decimal?.value ?? '.');
|
|
168
|
+
}
|
|
155
169
|
return value;
|
|
156
170
|
}
|
|
157
171
|
|
|
@@ -2,6 +2,8 @@ import type { PaginationFooterLabels } from '../PaginationFooter/PaginationFoote
|
|
|
2
2
|
|
|
3
3
|
/** Per-instance table chrome, including the composed PaginationFooter. Caller data stays caller-owned. */
|
|
4
4
|
export interface DataTableLabels extends PaginationFooterLabels {
|
|
5
|
+
booleanTrue: string;
|
|
6
|
+
booleanFalse: string;
|
|
5
7
|
/** Non-virtual table overflow region; virtualized regions keep the caller caption. */
|
|
6
8
|
scrollRegion: string;
|
|
7
9
|
selectionCount: (count: number) => string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CredentialDisclosureDialog,
|
|
3
|
+
type CredentialDisclosureDialogProps,
|
|
4
|
+
} from '../forms/CredentialDisclosureDialog/CredentialDisclosureDialog';
|
|
5
|
+
import { storedDisclosure } from '../forms/CredentialDisclosureDialog/fixtures';
|
|
6
|
+
|
|
7
|
+
/** Compile-only consumer fixture for every pre-amendment prop. */
|
|
8
|
+
export function LegacyCredentialDisclosure() {
|
|
9
|
+
const props: CredentialDisclosureDialogProps = {
|
|
10
|
+
disclosure: storedDisclosure,
|
|
11
|
+
onClose: () => {},
|
|
12
|
+
resolveReturnFocus: () => null,
|
|
13
|
+
fallbackFocus: () => null,
|
|
14
|
+
title: 'Credential',
|
|
15
|
+
description: 'Keep this value safe.',
|
|
16
|
+
credentialIdLabel: 'Identifier',
|
|
17
|
+
expiryLabel: 'Expiry',
|
|
18
|
+
secretLabel: 'Secret',
|
|
19
|
+
showLabel: 'Show',
|
|
20
|
+
hideLabel: 'Hide',
|
|
21
|
+
copyLabel: 'Copy',
|
|
22
|
+
copiedLabel: 'Copied',
|
|
23
|
+
successAnnouncement: 'Copied safely',
|
|
24
|
+
failureAnnouncement: 'Copy manually',
|
|
25
|
+
warningTitle: 'Save this value',
|
|
26
|
+
warningText: 'Shown once',
|
|
27
|
+
closeLabel: 'Close',
|
|
28
|
+
};
|
|
29
|
+
return <CredentialDisclosureDialog {...props} />;
|
|
30
|
+
}
|