@truenas/ui-components 0.7.5 → 0.7.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/fesm2022/truenas-ui-components.mjs +159 -18
- package/fesm2022/truenas-ui-components.mjs.map +1 -1
- package/package.json +1 -1
- package/scripts/jest.config.ts +9 -5
- package/scripts/release-config/commit-header-pattern.spec.ts +189 -0
- package/scripts/release-config/jest.config.ts +14 -0
- package/scripts/release-config/tsconfig.json +14 -0
- package/types/truenas-ui-components.d.ts +65 -5
package/package.json
CHANGED
package/scripts/jest.config.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import type { Config } from 'jest';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* `yarn test:scripts` — every build script under this
|
|
4
|
+
* `yarn test:scripts` — the repo's node-environment tests: every build script under this
|
|
5
|
+
* directory that has tests, plus `release-config`, which tests repo-root configuration
|
|
6
|
+
* rather than a script and lives here because this is where a node-environment Jest
|
|
7
|
+
* project already runs.
|
|
5
8
|
*
|
|
6
|
-
* One Jest project per
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* One Jest project per package rather than one config rooted here, because `<rootDir>`
|
|
10
|
+
* inside a project config resolves to that project's own directory: each package keeps
|
|
11
|
+
* pointing ts-jest at its own `tsconfig.json`, and adding the next one costs a line here
|
|
12
|
+
* rather than a merge of compiler options.
|
|
10
13
|
*/
|
|
11
14
|
const config: Config = {
|
|
12
15
|
projects: [
|
|
13
16
|
'<rootDir>/harness-docs/jest.config.ts',
|
|
14
17
|
'<rootDir>/icon-sprite/jest.config.ts',
|
|
18
|
+
'<rootDir>/release-config/jest.config.ts',
|
|
15
19
|
],
|
|
16
20
|
};
|
|
17
21
|
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PR title check and the release step both decide what a commit header means, from
|
|
3
|
+
* two patterns written in two files, and nothing made them agree. A scope naming two
|
|
4
|
+
* components with a comma — `feat(autocomplete,chip-input): …` — passed the title check,
|
|
5
|
+
* merged, and then matched no header pattern at all, so the commit analyzer read it as
|
|
6
|
+
* having no type: it triggered no release and appeared in no changelog (#315).
|
|
7
|
+
*
|
|
8
|
+
* These tests read both files rather than restating either pattern, so drift in either
|
|
9
|
+
* one fails here instead of silently dropping a merged commit from the next release.
|
|
10
|
+
*
|
|
11
|
+
* They live under the library's script tests because `yarn test:scripts` is where this
|
|
12
|
+
* repo runs Jest in a node environment; what they cover is repo-root release
|
|
13
|
+
* configuration, which is why they reach four directories up.
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
|
|
18
|
+
const repoRoot = join(__dirname, '..', '..', '..', '..');
|
|
19
|
+
|
|
20
|
+
interface ParserOpts {
|
|
21
|
+
headerPattern: string;
|
|
22
|
+
headerCorrespondence: string[];
|
|
23
|
+
breakingHeaderPattern: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type PluginEntry = string | [string, { parserOpts?: ParserOpts }];
|
|
27
|
+
|
|
28
|
+
const releaseConfig = JSON.parse(
|
|
29
|
+
readFileSync(join(repoRoot, '.releaserc.json'), 'utf8')
|
|
30
|
+
) as { plugins: PluginEntry[] };
|
|
31
|
+
|
|
32
|
+
/** Every semantic-release plugin that parses commit headers, with its plugin name. */
|
|
33
|
+
const parsingPlugins: [string, ParserOpts][] = [];
|
|
34
|
+
|
|
35
|
+
for (const plugin of releaseConfig.plugins) {
|
|
36
|
+
if (Array.isArray(plugin)) {
|
|
37
|
+
const [name, options] = plugin;
|
|
38
|
+
|
|
39
|
+
if (options.parserOpts) {
|
|
40
|
+
parsingPlugins.push([name, options.parserOpts]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The Bash pattern the PR title check runs, lifted from the workflow that runs it.
|
|
47
|
+
*
|
|
48
|
+
* `[[ =~ ]]` and `RegExp` both leave the end unanchored and the pattern anchors its own
|
|
49
|
+
* start, so a JS `RegExp` accepts what the check accepts for the constructs it uses:
|
|
50
|
+
* groups, alternation, character classes and `?`.
|
|
51
|
+
*/
|
|
52
|
+
function readPrTitlePattern(): RegExp {
|
|
53
|
+
const workflow = readFileSync(join(repoRoot, '.github/workflows/pr-title.yml'), 'utf8');
|
|
54
|
+
const assignment = /^\s*pattern='([^']+)'\s*$/m.exec(workflow);
|
|
55
|
+
|
|
56
|
+
if (!assignment) {
|
|
57
|
+
throw new Error('no `pattern=<...>` assignment found in .github/workflows/pr-title.yml');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return new RegExp(assignment[1]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const prTitleCheck = readPrTitlePattern();
|
|
64
|
+
|
|
65
|
+
/** What the release step reads out of a header, or null when it matches nothing. */
|
|
66
|
+
function parseHeader(opts: ParserOpts, header: string): Record<string, string | undefined> | null {
|
|
67
|
+
const match = new RegExp(opts.headerPattern).exec(header);
|
|
68
|
+
|
|
69
|
+
if (!match) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return Object.fromEntries(
|
|
74
|
+
opts.headerCorrespondence.map((field, index) => [field, match[index + 1]])
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const commaScopeHeader =
|
|
79
|
+
'NAS-142381 / 27.0.0-BETA.1 / feat(autocomplete,chip-input): drive options from an async [dataSource] (#312)';
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Headers the PR title check accepts. Every one is asserted to pass the check as well as
|
|
83
|
+
* to parse, so a case that stops being realistic fails rather than passing vacuously.
|
|
84
|
+
*/
|
|
85
|
+
const acceptedHeaders: { name: string; header: string; type: string; scope?: string }[] = [
|
|
86
|
+
{
|
|
87
|
+
name: 'a single-word scope',
|
|
88
|
+
header: 'feat(table): let a selection survive a dataSource change',
|
|
89
|
+
type: 'feat',
|
|
90
|
+
scope: 'table',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'the ticket and version prefix a squash commit carries',
|
|
94
|
+
header: 'NAS-143108 / 27.0.0-BETA.1 / fix(table): stop the pager aliasing pagination',
|
|
95
|
+
type: 'fix',
|
|
96
|
+
scope: 'table',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'no scope at all',
|
|
100
|
+
header: 'chore: bump the toolchain',
|
|
101
|
+
type: 'chore',
|
|
102
|
+
scope: undefined,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'two components separated by a comma (#315)',
|
|
106
|
+
header: commaScopeHeader,
|
|
107
|
+
type: 'feat',
|
|
108
|
+
scope: 'autocomplete,chip-input',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'a comma and a space between components',
|
|
112
|
+
header: 'feat(autocomplete, chip-input): drive options from an async [dataSource]',
|
|
113
|
+
type: 'feat',
|
|
114
|
+
scope: 'autocomplete, chip-input',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'a scope with a slash',
|
|
118
|
+
header: 'refactor(a11y/labels): fold the fallback into one helper',
|
|
119
|
+
type: 'refactor',
|
|
120
|
+
scope: 'a11y/labels',
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
describe('.releaserc.json', () => {
|
|
125
|
+
it('has more than one plugin parsing commit headers, one of them the commit analyzer', () => {
|
|
126
|
+
expect(parsingPlugins.length).toBeGreaterThan(1);
|
|
127
|
+
expect(parsingPlugins.map(([name]) => name)).toContain('@semantic-release/commit-analyzer');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('gives all of them the same parser options, so notes and version bumps agree', () => {
|
|
131
|
+
const [[, first]] = parsingPlugins;
|
|
132
|
+
|
|
133
|
+
// The plugin name rides along in the assertion so a failure says which one drifted.
|
|
134
|
+
for (const [name, opts] of parsingPlugins) {
|
|
135
|
+
expect([name, opts]).toEqual([name, first]);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe.each(parsingPlugins)('%s', (_pluginName, opts) => {
|
|
141
|
+
it.each(acceptedHeaders)('parses $name', ({ header, type, scope }) => {
|
|
142
|
+
expect(parseHeader(opts, header)).toMatchObject({ type, scope, subject: expect.any(String) });
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('reads a comma scope as one scope, leaving the subject whole', () => {
|
|
146
|
+
expect(parseHeader(opts, commaScopeHeader)?.subject).toBe(
|
|
147
|
+
'drive options from an async [dataSource] (#312)'
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('recognises a breaking change whose scope contains a comma', () => {
|
|
152
|
+
const breaking = new RegExp(opts.breakingHeaderPattern).exec(
|
|
153
|
+
'feat(autocomplete,chip-input)!: options are now a dataSource'
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
expect(breaking?.[1]).toBe('feat');
|
|
157
|
+
expect(breaking?.[2]).toBe('autocomplete,chip-input');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('the PR title check and the release step', () => {
|
|
162
|
+
it.each(acceptedHeaders)('agree on $name', ({ header, type }) => {
|
|
163
|
+
expect(prTitleCheck.test(header)).toBe(true);
|
|
164
|
+
|
|
165
|
+
for (const [, opts] of parsingPlugins) {
|
|
166
|
+
expect(parseHeader(opts, header)).toMatchObject({ type });
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('agree on every scope character the check allows, not a narrower set', () => {
|
|
171
|
+
// The check's scope is `\([^)]+\)`. A release pattern with a narrower scope class is
|
|
172
|
+
// what produced #315: the title passed, the commit merged, the release dropped it.
|
|
173
|
+
const scopes = ['a,b', 'a, b', 'a/b', 'a.b', 'a-b', 'a b', 'a:b', 'a+b', 'a&b', 'a#b', '@scope/pkg'];
|
|
174
|
+
|
|
175
|
+
for (const scope of scopes) {
|
|
176
|
+
const header = `feat(${scope}): a subject`;
|
|
177
|
+
|
|
178
|
+
expect(prTitleCheck.test(header)).toBe(true);
|
|
179
|
+
|
|
180
|
+
for (const [, opts] of parsingPlugins) {
|
|
181
|
+
expect(parseHeader(opts, header)).toEqual({
|
|
182
|
+
type: 'feat',
|
|
183
|
+
scope,
|
|
184
|
+
subject: 'a subject',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Config } from 'jest';
|
|
2
|
+
|
|
3
|
+
const config: Config = {
|
|
4
|
+
testEnvironment: 'node',
|
|
5
|
+
transform: {
|
|
6
|
+
'^.+\\.tsx?$': ['ts-jest', {
|
|
7
|
+
tsconfig: '<rootDir>/tsconfig.json',
|
|
8
|
+
}],
|
|
9
|
+
},
|
|
10
|
+
testMatch: ['<rootDir>/**/*.spec.ts'],
|
|
11
|
+
moduleFileExtensions: ['ts', 'js', 'json'],
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default config;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"isolatedModules": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"types": ["jest", "node"],
|
|
10
|
+
"rootDir": ".",
|
|
11
|
+
"outDir": "../../dist/out-tsc/scripts"
|
|
12
|
+
},
|
|
13
|
+
"include": ["**/*.ts"]
|
|
14
|
+
}
|
|
@@ -9686,6 +9686,33 @@ declare class TnTableComponent<T = unknown> implements OnInit {
|
|
|
9686
9686
|
* full list it holds, and call {@link clearSelection} once a batch action is done.
|
|
9687
9687
|
*/
|
|
9688
9688
|
selectionKey: _angular_core.InputSignal<((row: T) => unknown) | undefined>;
|
|
9689
|
+
/**
|
|
9690
|
+
* Identifies a row for EXPANSION, so an open detail row survives a `dataSource` change.
|
|
9691
|
+
*
|
|
9692
|
+
* Detail rows are held by object identity, and a consumer that re-fetches hands back new
|
|
9693
|
+
* objects for the same rows — so without a key a background reload closes the row the
|
|
9694
|
+
* user opened, which is usually the moment its contents are worth watching (a running
|
|
9695
|
+
* job's logs, say). With a key set, the table re-points the open rows at the new objects
|
|
9696
|
+
* instead of collapsing them.
|
|
9697
|
+
*
|
|
9698
|
+
* A row the new `data()` does not carry — another page, another tab, filtered out — is
|
|
9699
|
+
* kept, not dropped, and re-opens on its own once that row is listed again. Retention is
|
|
9700
|
+
* unconditional for the same reason it is for {@link selectionKey}: the table sees one
|
|
9701
|
+
* page and cannot tell a row that moved off-screen from one that is gone for good. Call
|
|
9702
|
+
* {@link clearExpansion} when the consumer knows it is gone.
|
|
9703
|
+
*
|
|
9704
|
+
* Without it the table keeps its historical behaviour — any `dataSource` reference change
|
|
9705
|
+
* collapses every open row.
|
|
9706
|
+
*
|
|
9707
|
+
* With a key set, drive expansion through {@link toggleRowExpansion}, {@link expandRow}
|
|
9708
|
+
* and {@link clearExpansion} rather than writing {@link expandedRows} directly, or the
|
|
9709
|
+
* retained set and the visible set drift apart.
|
|
9710
|
+
*
|
|
9711
|
+
* Pass a key that identifies the ROW, not its position, and bind a stable member rather
|
|
9712
|
+
* than writing the function in the template: `[expansionKey]="rowKey"` for a
|
|
9713
|
+
* `rowKey = (row: Job) => row.id` on the host.
|
|
9714
|
+
*/
|
|
9715
|
+
expansionKey: _angular_core.InputSignal<((row: T) => unknown) | undefined>;
|
|
9689
9716
|
emptyMessage: _angular_core.InputSignal<string>;
|
|
9690
9717
|
/**
|
|
9691
9718
|
* Optional second line under `emptyMessage`, for empty states that carry both a headline and an
|
|
@@ -9897,12 +9924,25 @@ declare class TnTableComponent<T = unknown> implements OnInit {
|
|
|
9897
9924
|
sortColumn: _angular_core.ModelSignal<string>;
|
|
9898
9925
|
sortDirection: _angular_core.ModelSignal<"" | "desc" | "asc">;
|
|
9899
9926
|
/**
|
|
9900
|
-
*
|
|
9901
|
-
*
|
|
9902
|
-
*
|
|
9903
|
-
*
|
|
9927
|
+
* The rows of the CURRENT `data()` whose detail row is open, by object identity.
|
|
9928
|
+
*
|
|
9929
|
+
* Replacing the data array collapses everything unless {@link expansionKey} is set, in
|
|
9930
|
+
* which case this is the visible slice of {@link expandedByKey} and survives the swap.
|
|
9931
|
+
*
|
|
9932
|
+
* Writable for consumers that predate {@link expansionKey}. **Adding a key to a consumer
|
|
9933
|
+
* that writes this signal directly is a migration, not a drop-in**: a write here closes
|
|
9934
|
+
* what is on screen but never touches the retained map, so every row whose key is still in
|
|
9935
|
+
* it re-opens on the next reconcile — the one it just closed included. Move those writes to
|
|
9936
|
+
* {@link expandRow}, {@link toggleRowExpansion} and {@link clearExpansion}, which keep the
|
|
9937
|
+
* two in step.
|
|
9904
9938
|
*/
|
|
9905
9939
|
expandedRows: _angular_core.WritableSignal<Set<unknown>>;
|
|
9940
|
+
/**
|
|
9941
|
+
* Every open detail row keyed by {@link expansionKey}, including rows no longer in
|
|
9942
|
+
* `data()`. Empty (and unused) when no key is set. Holds the row objects so a row that
|
|
9943
|
+
* comes back can be re-pointed at the object the new data carries.
|
|
9944
|
+
*/
|
|
9945
|
+
private expandedByKey;
|
|
9906
9946
|
private static instanceCount;
|
|
9907
9947
|
private readonly instanceId;
|
|
9908
9948
|
/**
|
|
@@ -9999,6 +10039,26 @@ declare class TnTableComponent<T = unknown> implements OnInit {
|
|
|
9999
10039
|
*/
|
|
10000
10040
|
canExpandRow(row: T): boolean;
|
|
10001
10041
|
toggleRowExpansion(row: T): void;
|
|
10042
|
+
/**
|
|
10043
|
+
* Whether `row`'s detail row counts as open for a toggle. With {@link expansionKey} that is
|
|
10044
|
+
* the retained map, which includes rows this page is not showing; without one it is object
|
|
10045
|
+
* identity against the visible set. Distinct from {@link isRowExpanded}, which drives
|
|
10046
|
+
* rendering and must therefore stay about the row in front of the user.
|
|
10047
|
+
*/
|
|
10048
|
+
private isExpansionOpen;
|
|
10049
|
+
/**
|
|
10050
|
+
* Opens `row`'s detail row programmatically — the same thing a chevron click does, for a
|
|
10051
|
+
* consumer restoring expansion from somewhere else (a `?jobId=` query parameter, say).
|
|
10052
|
+
* Honours `singleExpand` and keeps {@link expansionKey}'s retained set in step, which
|
|
10053
|
+
* writing {@link expandedRows} directly does not.
|
|
10054
|
+
*/
|
|
10055
|
+
expandRow(row: T): void;
|
|
10056
|
+
/**
|
|
10057
|
+
* Collapses every detail row, including rows retained under {@link expansionKey} that are
|
|
10058
|
+
* not on screen. `expandedRows.set(new Set())` only closes what is visible, so with a key
|
|
10059
|
+
* set the off-screen rows would re-open on the next reconcile.
|
|
10060
|
+
*/
|
|
10061
|
+
clearExpansion(): void;
|
|
10002
10062
|
isRowExpanded(row: T): boolean;
|
|
10003
10063
|
/**
|
|
10004
10064
|
* Whether the row element itself acts as the expand/collapse control. Requires `clickable` —
|
|
@@ -10226,7 +10286,7 @@ declare class TnTableComponent<T = unknown> implements OnInit {
|
|
|
10226
10286
|
getCardLabel(column: string): string;
|
|
10227
10287
|
getCellValue(row: T, column: string): unknown;
|
|
10228
10288
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnTableComponent<any>, never>;
|
|
10229
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnTableComponent<any>, "tn-table", never, { "dataSource": { "alias": "dataSource"; "required": false; "isSignal": true; }; "displayedColumns": { "alias": "displayedColumns"; "required": false; "isSignal": true; }; "trackBy": { "alias": "trackBy"; "required": false; "isSignal": true; }; "selectionKey": { "alias": "selectionKey"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "emptyDescription": { "alias": "emptyDescription"; "required": false; "isSignal": true; }; "emptyIcon": { "alias": "emptyIcon"; "required": false; "isSignal": true; }; "scrollRegionAriaLabel": { "alias": "scrollRegionAriaLabel"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "expandable": { "alias": "expandable"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; "isRowExpandable": { "alias": "isRowExpandable"; "required": false; "isSignal": true; }; "activeRow": { "alias": "activeRow"; "required": false; "isSignal": true; }; "activeWhen": { "alias": "activeWhen"; "required": false; "isSignal": true; }; "activeBg": { "alias": "activeBg"; "required": false; "isSignal": true; }; "activeIndicator": { "alias": "activeIndicator"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "loadingMessage": { "alias": "loadingMessage"; "required": false; "isSignal": true; }; "clickable": { "alias": "clickable"; "required": false; "isSignal": true; }; "expandOnRowClick": { "alias": "expandOnRowClick"; "required": false; "isSignal": true; }; "singleExpand": { "alias": "singleExpand"; "required": false; "isSignal": true; }; "fixedLayout": { "alias": "fixedLayout"; "required": false; "isSignal": true; }; "minColumnWidth": { "alias": "minColumnWidth"; "required": false; "isSignal": true; }; "minWidth": { "alias": "minWidth"; "required": false; "isSignal": true; }; "mobileLayout": { "alias": "mobileLayout"; "required": false; "isSignal": true; }; "cardBreakpoint": { "alias": "cardBreakpoint"; "required": false; "isSignal": true; }; "cardPrimaryCount": { "alias": "cardPrimaryCount"; "required": false; "isSignal": true; }; "sortColumn": { "alias": "sortColumn"; "required": false; "isSignal": true; }; "sortDirection": { "alias": "sortDirection"; "required": false; "isSignal": true; }; }, { "sortChange": "sortChange"; "selectionChange": "selectionChange"; "rowClick": "rowClick"; "rowDoubleClick": "rowDoubleClick"; "sortColumn": "sortColumnChange"; "sortDirection": "sortDirectionChange"; }, ["columnDefs", "detailRowDef", "rowActionsDef"], never, true, [{ directive: typeof TnTestIdDirective; inputs: { "tnTestId": "testId"; }; outputs: {}; }]>;
|
|
10289
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnTableComponent<any>, "tn-table", never, { "dataSource": { "alias": "dataSource"; "required": false; "isSignal": true; }; "displayedColumns": { "alias": "displayedColumns"; "required": false; "isSignal": true; }; "trackBy": { "alias": "trackBy"; "required": false; "isSignal": true; }; "selectionKey": { "alias": "selectionKey"; "required": false; "isSignal": true; }; "expansionKey": { "alias": "expansionKey"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "emptyDescription": { "alias": "emptyDescription"; "required": false; "isSignal": true; }; "emptyIcon": { "alias": "emptyIcon"; "required": false; "isSignal": true; }; "scrollRegionAriaLabel": { "alias": "scrollRegionAriaLabel"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "expandable": { "alias": "expandable"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; "isRowExpandable": { "alias": "isRowExpandable"; "required": false; "isSignal": true; }; "activeRow": { "alias": "activeRow"; "required": false; "isSignal": true; }; "activeWhen": { "alias": "activeWhen"; "required": false; "isSignal": true; }; "activeBg": { "alias": "activeBg"; "required": false; "isSignal": true; }; "activeIndicator": { "alias": "activeIndicator"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "loadingMessage": { "alias": "loadingMessage"; "required": false; "isSignal": true; }; "clickable": { "alias": "clickable"; "required": false; "isSignal": true; }; "expandOnRowClick": { "alias": "expandOnRowClick"; "required": false; "isSignal": true; }; "singleExpand": { "alias": "singleExpand"; "required": false; "isSignal": true; }; "fixedLayout": { "alias": "fixedLayout"; "required": false; "isSignal": true; }; "minColumnWidth": { "alias": "minColumnWidth"; "required": false; "isSignal": true; }; "minWidth": { "alias": "minWidth"; "required": false; "isSignal": true; }; "mobileLayout": { "alias": "mobileLayout"; "required": false; "isSignal": true; }; "cardBreakpoint": { "alias": "cardBreakpoint"; "required": false; "isSignal": true; }; "cardPrimaryCount": { "alias": "cardPrimaryCount"; "required": false; "isSignal": true; }; "sortColumn": { "alias": "sortColumn"; "required": false; "isSignal": true; }; "sortDirection": { "alias": "sortDirection"; "required": false; "isSignal": true; }; }, { "sortChange": "sortChange"; "selectionChange": "selectionChange"; "rowClick": "rowClick"; "rowDoubleClick": "rowDoubleClick"; "sortColumn": "sortColumnChange"; "sortDirection": "sortDirectionChange"; }, ["columnDefs", "detailRowDef", "rowActionsDef"], never, true, [{ directive: typeof TnTestIdDirective; inputs: { "tnTestId": "testId"; }; outputs: {}; }]>;
|
|
10230
10290
|
}
|
|
10231
10291
|
|
|
10232
10292
|
/**
|