@finos/legend-application-studio 28.21.3 → 28.21.5

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.
Files changed (66) hide show
  1. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.d.ts +1 -1
  2. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.d.ts.map +1 -1
  3. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js +3 -3
  4. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js.map +1 -1
  5. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts +3 -0
  6. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts.map +1 -1
  7. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js +72 -52
  8. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js.map +1 -1
  9. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
  10. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +22 -1
  11. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
  12. package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.d.ts +23 -0
  13. package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.d.ts.map +1 -0
  14. package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js +267 -0
  15. package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js.map +1 -0
  16. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.d.ts.map +1 -1
  17. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.js +113 -75
  18. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.js.map +1 -1
  19. package/lib/components/editor/editor-group/testable/TestableSharedComponents.d.ts.map +1 -1
  20. package/lib/components/editor/editor-group/testable/TestableSharedComponents.js +39 -5
  21. package/lib/components/editor/editor-group/testable/TestableSharedComponents.js.map +1 -1
  22. package/lib/components/editor/side-bar/DevMetadataPanel.d.ts.map +1 -1
  23. package/lib/components/editor/side-bar/DevMetadataPanel.js +37 -6
  24. package/lib/components/editor/side-bar/DevMetadataPanel.js.map +1 -1
  25. package/lib/index.css +2 -2
  26. package/lib/index.css.map +1 -1
  27. package/lib/package.json +1 -1
  28. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts +17 -2
  29. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts.map +1 -1
  30. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js +56 -49
  31. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js.map +1 -1
  32. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts +4 -1
  33. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts.map +1 -1
  34. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js +4 -0
  35. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
  36. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts +113 -0
  37. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts.map +1 -0
  38. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js +647 -0
  39. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js.map +1 -0
  40. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts +18 -4
  41. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts.map +1 -1
  42. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js +214 -53
  43. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js.map +1 -1
  44. package/lib/stores/editor/editor-state/element-editor-state/testable/TestAssertionState.d.ts +17 -1
  45. package/lib/stores/editor/editor-state/element-editor-state/testable/TestAssertionState.d.ts.map +1 -1
  46. package/lib/stores/editor/editor-state/element-editor-state/testable/TestAssertionState.js +46 -1
  47. package/lib/stores/editor/editor-state/element-editor-state/testable/TestAssertionState.js.map +1 -1
  48. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.d.ts +9 -0
  49. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.d.ts.map +1 -1
  50. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.js +55 -0
  51. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.js.map +1 -1
  52. package/package.json +16 -16
  53. package/src/components/editor/editor-group/data-editor/EmbeddedDataEditor.tsx +3 -0
  54. package/src/components/editor/editor-group/data-editor/RelationElementsDataEditor.tsx +331 -231
  55. package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +32 -0
  56. package/src/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.tsx +935 -0
  57. package/src/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.tsx +425 -308
  58. package/src/components/editor/editor-group/testable/TestableSharedComponents.tsx +160 -15
  59. package/src/components/editor/side-bar/DevMetadataPanel.tsx +194 -10
  60. package/src/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.ts +82 -51
  61. package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +4 -0
  62. package/src/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.ts +927 -0
  63. package/src/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.ts +303 -72
  64. package/src/stores/editor/editor-state/element-editor-state/testable/TestAssertionState.ts +66 -0
  65. package/src/stores/editor/sidebar-state/dev-metadata/DevMetadataState.ts +76 -0
  66. package/tsconfig.json +2 -0
@@ -48,6 +48,14 @@ const NewRelationElementModal = observer(
48
48
  (props: { dataState: RelationElementsDataState; isReadOnly: boolean }) => {
49
49
  const { isReadOnly, dataState } = props;
50
50
  const applicationStore = dataState.editorStore.applicationStore;
51
+ const availableAccessorOptions = dataState.availableAccessorOptions;
52
+ const hasAccessorOptions =
53
+ dataState.accessorOptions !== undefined &&
54
+ availableAccessorOptions.length > 0;
55
+
56
+ const [selectedAccessor, setSelectedAccessor] = useState<
57
+ string | undefined
58
+ >(availableAccessorOptions[0]?.value);
51
59
 
52
60
  enum PathType {
53
61
  SCHEMA_TABLE = 'Schema and Table',
@@ -83,17 +91,29 @@ const NewRelationElementModal = observer(
83
91
  const closeModal = (): void =>
84
92
  dataState.setShowNewRelationElementModal(false);
85
93
  const handleSubmit = (): void => {
86
- const path: string[] = [];
87
- if (pathType && schemaName && tableName) {
88
- path.push(schemaName);
89
- path.push(tableName);
94
+ if (hasAccessorOptions && selectedAccessor) {
95
+ const option = availableAccessorOptions.find(
96
+ (o) => o.value === selectedAccessor,
97
+ );
98
+ if (option) {
99
+ const relationElement = new RelationElement();
100
+ relationElement.paths = [option.value];
101
+ relationElement.columns = option.columns;
102
+ relationElement.rows = [];
103
+ dataState.addRelationElement(relationElement);
104
+ }
105
+ } else {
106
+ const path: string[] = [];
107
+ if (pathType && schemaName && tableName) {
108
+ path.push(schemaName);
109
+ path.push(tableName);
110
+ }
111
+ const relationElement = new RelationElement();
112
+ relationElement.columns = [];
113
+ relationElement.rows = [];
114
+ relationElement.paths = path;
115
+ dataState.addRelationElement(relationElement);
90
116
  }
91
- const relationElement = new RelationElement();
92
- relationElement.columns = [];
93
- relationElement.rows = [];
94
- relationElement.paths = path;
95
-
96
- dataState.addRelationElement(relationElement);
97
117
  closeModal();
98
118
  };
99
119
 
@@ -112,48 +132,84 @@ const NewRelationElementModal = observer(
112
132
  onSubmit={(event) => {
113
133
  event.preventDefault();
114
134
  handleSubmit();
115
- closeModal();
116
135
  }}
117
136
  className="modal modal--dark search-modal"
118
137
  >
119
138
  <div className="modal__title">Add Relation Element</div>
120
139
  <div className="relational-data-editor__identifier">
121
- <div className="relational-data-editor__identifier__values">
122
- <CustomSelectorInput
123
- className="explorer__new-element-modal__driver__dropdown"
124
- options={pathTypeOptions}
125
- onChange={onPathTypeChange}
126
- value={pathType}
127
- isClearable={false}
128
- darkMode={
129
- !applicationStore.layoutService
130
- .TEMPORARY__isLightColorThemeEnabled
131
- }
132
- />
133
- </div>
134
- <>
135
- <div className="relational-data-editor__identifier__values">
136
- <input
137
- className="panel__content__form__section__input"
138
- disabled={isReadOnly}
139
- placeholder="schemaName"
140
- value={schemaName}
141
- onChange={changeSchemaValue}
142
- />
143
- </div>
140
+ {hasAccessorOptions ? (
144
141
  <div className="relational-data-editor__identifier__values">
145
- <input
146
- className="relational-data-editor__identifier__values panel__content__form__section__input"
147
- disabled={isReadOnly}
148
- placeholder="tableName"
149
- value={tableName}
150
- onChange={changeTableValue}
142
+ <div className="panel__content__form__section__header__label">
143
+ {dataState.accessorTypeLabel}
144
+ </div>
145
+ <CustomSelectorInput
146
+ className="explorer__new-element-modal__driver__dropdown"
147
+ options={availableAccessorOptions.map((o) => ({
148
+ value: o.value,
149
+ label: o.label,
150
+ }))}
151
+ onChange={(
152
+ val: { value: string; label: string } | null,
153
+ ): void => {
154
+ setSelectedAccessor(val?.value);
155
+ }}
156
+ value={
157
+ selectedAccessor
158
+ ? {
159
+ value: selectedAccessor,
160
+ label: selectedAccessor,
161
+ }
162
+ : null
163
+ }
164
+ placeholder={`Select ${dataState.accessorTypeLabel ?? 'option'}...`}
165
+ isClearable={false}
166
+ darkMode={
167
+ !applicationStore.layoutService
168
+ .TEMPORARY__isLightColorThemeEnabled
169
+ }
151
170
  />
152
171
  </div>
153
- </>
172
+ ) : (
173
+ <>
174
+ <div className="relational-data-editor__identifier__values">
175
+ <CustomSelectorInput
176
+ className="explorer__new-element-modal__driver__dropdown"
177
+ options={pathTypeOptions}
178
+ onChange={onPathTypeChange}
179
+ value={pathType}
180
+ isClearable={false}
181
+ darkMode={
182
+ !applicationStore.layoutService
183
+ .TEMPORARY__isLightColorThemeEnabled
184
+ }
185
+ />
186
+ </div>
187
+ <div className="relational-data-editor__identifier__values">
188
+ <input
189
+ className="panel__content__form__section__input"
190
+ disabled={isReadOnly}
191
+ placeholder="schemaName"
192
+ value={schemaName}
193
+ onChange={changeSchemaValue}
194
+ />
195
+ </div>
196
+ <div className="relational-data-editor__identifier__values">
197
+ <input
198
+ className="relational-data-editor__identifier__values panel__content__form__section__input"
199
+ disabled={isReadOnly}
200
+ placeholder="tableName"
201
+ value={tableName}
202
+ onChange={changeTableValue}
203
+ />
204
+ </div>
205
+ </>
206
+ )}
154
207
  </div>
155
208
  <div className="search-modal__actions">
156
- <button className="btn btn--dark" disabled={isReadOnly}>
209
+ <button
210
+ className="btn btn--dark"
211
+ disabled={isReadOnly || (hasAccessorOptions && !selectedAccessor)}
212
+ >
157
213
  Add
158
214
  </button>
159
215
  </div>
@@ -167,30 +223,30 @@ export const RelationElementEditor = observer(
167
223
  (props: {
168
224
  relationElementState: RelationElementState;
169
225
  isReadOnly: boolean;
226
+ hideColumnDefinitions?: boolean;
170
227
  }) => {
171
- const { relationElementState, isReadOnly } = props;
228
+ const { relationElementState, isReadOnly, hideColumnDefinitions } = props;
172
229
  const editorStore = useEditorStore();
173
230
  const embeddedData = relationElementState.relationElement;
174
- const [exportFormat, setExportFormat] = useState<'json' | 'csv' | 'sql'>(
175
- 'json',
176
- );
231
+ const canEditColumns =
232
+ !isReadOnly && relationElementState.supportsColumnEditing;
177
233
  const fileInputRef = useRef<HTMLInputElement>(null);
178
234
 
179
235
  const addColumn = (): void => {
180
- if (!isReadOnly) {
236
+ if (canEditColumns) {
181
237
  const columnName = `column_${embeddedData.columns.length + 1}`;
182
238
  relationElementState.addColumn(columnName);
183
239
  }
184
240
  };
185
241
 
186
242
  const removeColumn = (index: number): void => {
187
- if (!isReadOnly) {
243
+ if (canEditColumns) {
188
244
  relationElementState.removeColumn(index);
189
245
  }
190
246
  };
191
247
 
192
248
  const updateColumn = (index: number, value: string): void => {
193
- if (!isReadOnly) {
249
+ if (canEditColumns) {
194
250
  const column = embeddedData.columns[index];
195
251
  if (column) {
196
252
  relationElementState.updateColumn(index, value);
@@ -239,39 +295,13 @@ export const RelationElementEditor = observer(
239
295
  }
240
296
  };
241
297
 
242
- const exportData = (): void => {
243
- let content = '';
244
- let filename = '';
245
- let mimeType = '';
246
-
247
- switch (exportFormat) {
248
- case 'json':
249
- content = relationElementState.exportJSON();
250
- filename = 'test_data.json';
251
- mimeType = 'application/json';
252
- break;
253
- case 'csv':
254
- content = relationElementState.exportCSV();
255
- filename = 'test_data.csv';
256
- mimeType = 'text/csv';
257
- break;
258
- case 'sql':
259
- content = relationElementState.exportSQL();
260
- filename = 'test_data.sql';
261
- mimeType = 'text/sql';
262
- break;
263
- default:
264
- content = relationElementState.exportJSON();
265
- filename = 'test_data.json';
266
- mimeType = 'application/json';
267
- break;
268
- }
269
-
270
- const blob = new Blob([content], { type: mimeType });
298
+ const exportCSV = (): void => {
299
+ const content = relationElementState.exportCSV();
300
+ const blob = new Blob([content], { type: 'text/csv' });
271
301
  const url = URL.createObjectURL(blob);
272
302
  const a = document.createElement('a');
273
303
  a.href = url;
274
- a.download = filename;
304
+ a.download = 'test_data.csv';
275
305
  document.body.appendChild(a);
276
306
  a.click();
277
307
  document.body.removeChild(a);
@@ -301,53 +331,101 @@ export const RelationElementEditor = observer(
301
331
 
302
332
  return (
303
333
  <div className="relation-test-data-editor__content">
304
- <div className="relation-test-data-editor__columns">
334
+ <div className="relation-test-data-editor__data">
305
335
  <div className="relation-test-data-editor__section-header">
306
- <div className="relation-test-data-editor__section-title">
307
- Column Definitions
336
+ <div className="relation-test-data-editor__section-header__left">
337
+ <div className="relation-test-data-editor__section-title">
338
+ Test Data ({embeddedData.rows.length} rows)
339
+ </div>
308
340
  </div>
309
- <button
310
- className="btn--icon btn--dark btn--sm"
311
- onClick={addColumn}
312
- disabled={isReadOnly}
313
- title="Add Column"
314
- >
315
- <PlusIcon />
316
- </button>
317
- </div>
318
- <div className="relation-test-data-editor__columns-grid">
319
- {embeddedData.columns.map((column, index) => (
320
- <div
321
- key={`column-${guaranteeNonNullable(index)}`}
322
- className="relation-test-data-editor__column-row"
323
- >
324
- <input
325
- className="relation-test-data-editor__column-input"
326
- type="text"
327
- value={column}
328
- onChange={(e) => updateColumn(index, e.target.value)}
329
- placeholder="Column Name"
330
- disabled={isReadOnly}
331
- />
332
- <button
333
- className="btn--icon btn--caution btn--dark btn--sm"
334
- onClick={() => removeColumn(index)}
335
- disabled={isReadOnly}
336
- title="Remove Column"
337
- >
338
- <TimesIcon />
339
- </button>
341
+ <div className="relation-test-data-editor__toolbar">
342
+ <div className="relation-test-data-editor__toolbar__left">
343
+ <div className="relation-test-data-editor__action-control">
344
+ <span className="relation-test-data-editor__action-control__label">
345
+ Add Column
346
+ </span>
347
+ <button
348
+ className="btn--icon btn--dark btn--sm"
349
+ onClick={addColumn}
350
+ disabled={!canEditColumns}
351
+ title="Add Column"
352
+ >
353
+ <PlusIcon />
354
+ </button>
355
+ </div>
356
+ <div className="relation-test-data-editor__action-control">
357
+ <span className="relation-test-data-editor__action-control__label">
358
+ Add Row
359
+ </span>
360
+ <button
361
+ className="btn--icon btn--dark btn--sm"
362
+ onClick={addRow}
363
+ disabled={isReadOnly || embeddedData.columns.length === 0}
364
+ title="Add Row"
365
+ >
366
+ <PlusIcon />
367
+ </button>
368
+ </div>
369
+ </div>
370
+ <div className="relation-test-data-editor__toolbar__right">
371
+ <div className="relation-test-data-editor__import-controls">
372
+ <span className="relation-test-data-editor__import-controls__label">
373
+ Upload CSV
374
+ </span>
375
+ <input
376
+ ref={fileInputRef}
377
+ type="file"
378
+ accept=".csv"
379
+ onChange={handleFileUpload}
380
+ style={{ display: 'none' }}
381
+ disabled={isReadOnly}
382
+ />
383
+ <button
384
+ className="btn--icon btn--dark btn--sm"
385
+ onClick={() => fileInputRef.current?.click()}
386
+ disabled={isReadOnly}
387
+ title="Upload CSV"
388
+ >
389
+ <UploadIcon />
390
+ </button>
391
+ </div>
340
392
  </div>
341
- ))}
342
- </div>
343
- </div>
344
-
345
- <div className="relation-test-data-editor__data">
346
- <div className="relation-test-data-editor__section-header">
347
- <div className="relation-test-data-editor__section-title">
348
- Test Data ({embeddedData.rows.length} rows)
349
393
  </div>
350
394
  </div>
395
+ {!hideColumnDefinitions ? (
396
+ <div className="relation-test-data-editor__columns">
397
+ <div className="relation-test-data-editor__section-header">
398
+ <div className="relation-test-data-editor__section-title">
399
+ Column Definitions
400
+ </div>
401
+ </div>
402
+ <div className="relation-test-data-editor__columns-grid">
403
+ {embeddedData.columns.map((column, index) => (
404
+ <div
405
+ key={`column-${guaranteeNonNullable(index)}`}
406
+ className="relation-test-data-editor__column-row"
407
+ >
408
+ <input
409
+ className="relation-test-data-editor__column-input"
410
+ type="text"
411
+ value={column}
412
+ onChange={(e) => updateColumn(index, e.target.value)}
413
+ placeholder="Column Name"
414
+ disabled={!canEditColumns}
415
+ />
416
+ <button
417
+ className="btn--icon btn--caution btn--dark btn--sm"
418
+ onClick={() => removeColumn(index)}
419
+ disabled={!canEditColumns}
420
+ title="Remove Column"
421
+ >
422
+ <TimesIcon />
423
+ </button>
424
+ </div>
425
+ ))}
426
+ </div>
427
+ </div>
428
+ ) : null}
351
429
  {embeddedData.rows.length === 0 ? (
352
430
  <div className="relation-test-data-editor__empty-data">
353
431
  <div className="relation-test-data-editor__empty-text">
@@ -357,71 +435,82 @@ export const RelationElementEditor = observer(
357
435
  </div>
358
436
  ) : (
359
437
  <div className="relation-test-data-editor__data-grid">
360
- <div className="relation-test-data-editor__data-header">
361
- {embeddedData.columns.map((column) => (
362
- <div
363
- key={column}
364
- className="relation-test-data-editor__data-header-cell"
365
- >
366
- {column}
367
- {/* <span className="relation-test-data-editor__data-type">
368
- ({column.type})
369
- </span> */}
370
- </div>
371
- ))}
372
- <div className="relation-test-data-editor__data-header-cell relation-test-data-editor__data-actions">
373
- Actions
374
- </div>
375
- </div>
376
- {embeddedData.rows.map((row, rowIndex) => (
377
- <div
378
- key={`row-${guaranteeNonNullable(rowIndex)}`}
379
- className="relation-test-data-editor__data-row"
380
- >
381
- {embeddedData.columns.map((column, columnIndex) => (
382
- <div
383
- key={column}
384
- className="relation-test-data-editor__data-cell"
438
+ <table className="relation-test-data-editor__table">
439
+ <thead>
440
+ <tr>
441
+ {embeddedData.columns.map((column, columnIndex) => (
442
+ <th
443
+ key={column}
444
+ className="relation-test-data-editor__th"
445
+ >
446
+ <div className="relation-test-data-editor__th__inner">
447
+ <span className="relation-test-data-editor__th__label">
448
+ {column}
449
+ </span>
450
+ <button
451
+ className="relation-test-data-editor__th__delete"
452
+ onClick={() => removeColumn(columnIndex)}
453
+ disabled={!canEditColumns}
454
+ title={`Remove column ${column}`}
455
+ >
456
+ <TimesIcon />
457
+ </button>
458
+ </div>
459
+ </th>
460
+ ))}
461
+ <th className="relation-test-data-editor__th relation-test-data-editor__th--actions" />
462
+ </tr>
463
+ </thead>
464
+ <tbody>
465
+ {embeddedData.rows.map((_row, rowIndex) => (
466
+ <tr
467
+ key={`row-${guaranteeNonNullable(rowIndex)}`}
468
+ className="relation-test-data-editor__tr"
385
469
  >
386
- <input
387
- type="text"
388
- value={row.values[columnIndex] ?? ''}
389
- onChange={(e) =>
390
- updateCellValue(rowIndex, columnIndex, e.target.value)
391
- }
392
- disabled={isReadOnly}
393
- className="relation-test-data-editor__data-input"
394
- />
395
- </div>
470
+ {embeddedData.columns.map((column, columnIndex) => (
471
+ <td
472
+ key={column}
473
+ className="relation-test-data-editor__td"
474
+ >
475
+ <input
476
+ type="text"
477
+ value={relationElementState.getDisplayValue(
478
+ rowIndex,
479
+ columnIndex,
480
+ )}
481
+ onChange={(e) =>
482
+ updateCellValue(
483
+ rowIndex,
484
+ columnIndex,
485
+ e.target.value,
486
+ )
487
+ }
488
+ disabled={isReadOnly}
489
+ className="relation-test-data-editor__data-input"
490
+ />
491
+ </td>
492
+ ))}
493
+ <td className="relation-test-data-editor__td relation-test-data-editor__td--actions">
494
+ <button
495
+ className="btn--icon btn--caution btn--dark btn--sm relation-test-data-editor__icon-button--compact"
496
+ onClick={() => removeRow(rowIndex)}
497
+ disabled={isReadOnly}
498
+ title="Remove Row"
499
+ >
500
+ <TimesIcon />
501
+ </button>
502
+ </td>
503
+ </tr>
396
504
  ))}
397
- <div className="relation-test-data-editor__data-cell relation-test-data-editor__data-actions">
398
- <button
399
- className="btn--icon btn--caution btn--dark btn--sm"
400
- onClick={() => removeRow(rowIndex)}
401
- disabled={isReadOnly}
402
- title="Remove Row"
403
- >
404
- <TimesIcon />
405
- </button>
406
- </div>
407
- </div>
408
- ))}
505
+ </tbody>
506
+ </table>
409
507
  </div>
410
508
  )}
411
509
 
412
- <div className="relation-test-data-editor__export-controls">
413
- <div className="relation-test-data-editor__export-controls__btn-group">
510
+ <div className="relation-test-data-editor__footer-actions">
511
+ <div className="relation-test-data-editor__footer-actions__left">
414
512
  <button
415
513
  className="btn--icon btn--dark btn--sm"
416
- onClick={addRow}
417
- disabled={isReadOnly || embeddedData.columns.length === 0}
418
- title="Add Row"
419
- >
420
- <PlusIcon />
421
- </button>
422
-
423
- <button
424
- className="btn--icon btn--caution btn--dark btn--sm"
425
514
  onClick={handleClearData}
426
515
  disabled={isReadOnly || embeddedData.rows.length === 0}
427
516
  title="Clear All Data"
@@ -429,48 +518,20 @@ export const RelationElementEditor = observer(
429
518
  <TrashIcon />
430
519
  </button>
431
520
  </div>
432
- <div className="relation-test-data-editor__export-format">
433
- <label htmlFor="exportFormat">Export as:</label>
434
- <select
435
- id="exportFormat"
436
- value={exportFormat}
437
- onChange={(e) =>
438
- setExportFormat(e.target.value as 'json' | 'csv' | 'sql')
439
- }
440
- disabled={isReadOnly}
441
- className="relation-test-data-editor__export-select"
442
- >
443
- <option value="json">JSON</option>
444
- <option value="csv">CSV</option>
445
- <option value="sql">SQL INSERT</option>
446
- </select>
447
- <button
448
- className="btn--icon btn--dark btn--sm"
449
- onClick={exportData}
450
- disabled={isReadOnly}
451
- title={`Export as ${exportFormat.toUpperCase()}`}
452
- >
453
- <FileImportIcon />
454
- </button>
455
- </div>
456
-
457
- <div className="relation-test-data-editor__import-controls">
458
- <input
459
- ref={fileInputRef}
460
- type="file"
461
- accept=".csv"
462
- onChange={handleFileUpload}
463
- style={{ display: 'none' }}
464
- disabled={isReadOnly}
465
- />
466
- <button
467
- className="btn--icon btn--dark btn--sm"
468
- onClick={() => fileInputRef.current?.click()}
469
- disabled={isReadOnly}
470
- title="Upload a file of CSV"
471
- >
472
- <UploadIcon />
473
- </button>
521
+ <div className="relation-test-data-editor__footer-actions__right">
522
+ <div className="relation-test-data-editor__action-control">
523
+ <span className="relation-test-data-editor__action-control__label">
524
+ Export CSV
525
+ </span>
526
+ <button
527
+ className="btn--icon btn--dark btn--sm"
528
+ onClick={exportCSV}
529
+ disabled={isReadOnly}
530
+ title="Export as CSV"
531
+ >
532
+ <FileImportIcon />
533
+ </button>
534
+ </div>
474
535
  </div>
475
536
  </div>
476
537
  </div>
@@ -480,15 +541,37 @@ export const RelationElementEditor = observer(
480
541
  );
481
542
 
482
543
  export const RelationElementsDataEditor = observer(
483
- (props: { dataState: RelationElementsDataState; isReadOnly: boolean }) => {
484
- const { dataState, isReadOnly } = props;
544
+ (props: {
545
+ dataState: RelationElementsDataState;
546
+ isReadOnly: boolean;
547
+ isSharedData?: boolean | undefined;
548
+ hideColumnDefinitions?: boolean;
549
+ }) => {
550
+ const { dataState, isReadOnly, isSharedData, hideColumnDefinitions } =
551
+ props;
485
552
 
486
553
  useApplicationNavigationContext(
487
554
  LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.EMBEDDED_DATA_RELATIONAL_EDITOR,
488
555
  );
489
556
 
490
557
  const addRelationElement = (): void => {
491
- dataState.setShowNewRelationElementModal(true);
558
+ const doAdd = (): void => {
559
+ if (
560
+ dataState.accessorOptions !== undefined &&
561
+ dataState.availableAccessorOptions.length === 0
562
+ ) {
563
+ dataState.editorStore.applicationStore.notificationService.notifyWarning(
564
+ `All referenced ${dataState.accessorTypeLabel ?? 'item'}s' data is already present`,
565
+ );
566
+ return;
567
+ }
568
+ dataState.setShowNewRelationElementModal(true);
569
+ };
570
+ if (dataState.refreshAccessorOptions) {
571
+ dataState.refreshAccessorOptions().then(doAdd).catch(doAdd);
572
+ } else {
573
+ doAdd();
574
+ }
492
575
  };
493
576
 
494
577
  const changeRelationElement = (
@@ -532,16 +615,30 @@ export const RelationElementsDataEditor = observer(
532
615
  ) : (
533
616
  <span>{relationElementState.relationElement.paths[0]}</span>
534
617
  )}
618
+ {!isReadOnly && !isSharedData && (
619
+ <button
620
+ className="service-editor__tab__close-btn"
621
+ onClick={(e): void => {
622
+ e.stopPropagation();
623
+ dataState.deleteRelationElement(relationElementState);
624
+ }}
625
+ title="Delete"
626
+ >
627
+ <TimesIcon />
628
+ </button>
629
+ )}
535
630
  </div>
536
631
  ))}
537
632
  </div>
538
- <button
539
- onClick={addRelationElement}
540
- disabled={isReadOnly}
541
- title="Add Relation Element"
542
- >
543
- <PlusIcon />
544
- </button>
633
+ {!isSharedData && (
634
+ <button
635
+ onClick={addRelationElement}
636
+ disabled={isReadOnly}
637
+ title="Add Relation Element"
638
+ >
639
+ <PlusIcon />
640
+ </button>
641
+ )}
545
642
  </div>
546
643
  {dataState.relationElementStates.length === 0 ||
547
644
  dataState.activeRelationElement === undefined ? (
@@ -556,6 +653,9 @@ export const RelationElementsDataEditor = observer(
556
653
  <RelationElementEditor
557
654
  relationElementState={dataState.activeRelationElement}
558
655
  isReadOnly={isReadOnly}
656
+ {...(hideColumnDefinitions !== undefined
657
+ ? { hideColumnDefinitions }
658
+ : {})}
559
659
  />
560
660
  )}
561
661
  </PanelContent>