Skip to content

Commit f4155b8

Browse files
authored
Use gqlSubscribable .loading/.error stores across UI (#1885)
* Use gqlSubscribable .loading/.error stores across UI * Replace null-check loading patterns and derived loading stores with the .loading sub-stores exposed by gqlSubscribable. * Add error handling via AsyncContentState to list-based panels (Constraints, Scheduling Goals/Conditions, PlanForm snapshots, Actions). * Remove T[] | null types from array stores in favor of T[] defaults. * Fix DataGrid to properly show no-rows overlay after loading completes. * Fix subscribable error handler to clear loading state on subscription errors.
1 parent 4653156 commit f4155b8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+688
-624
lines changed

e2e-tests/fixtures/Constraints.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export class Constraints {
1010
constraintDefinition: string = `export default function peelGreaterThanOrEqual3(): Constraint { return Real.Resource('/peel').greaterThanOrEqual(3); }`;
1111
constraintDescription: string = 'This is a constraint description.';
1212
constraintName: string;
13-
emptyConstraintsLabel: Locator;
13+
1414
inputConstraintDefinition: Locator;
1515
inputConstraintDescription: Locator;
1616
inputConstraintModel: Locator;
@@ -90,7 +90,7 @@ export class Constraints {
9090

9191
async goto() {
9292
await this.page.goto('/constraints', { waitUntil: 'networkidle' });
93-
await expect(this.table.or(this.emptyConstraintsLabel)).toBeVisible();
93+
await expect(this.table).toBeVisible();
9494
}
9595

9696
async gotoNew() {
@@ -101,7 +101,6 @@ export class Constraints {
101101
this.closeButton = page.locator(`button:has-text("Close")`);
102102
this.confirmModal = page.locator(`.modal:has-text("Delete Constraint")`);
103103
this.confirmModalDeleteButton = this.confirmModal.getByRole('button', { name: 'Delete' });
104-
this.emptyConstraintsLabel = page.getByText('No Constraints Found');
105104
this.inputConstraintDefinition = page.locator('.monaco-editor >> textarea.inputarea');
106105
this.inputConstraintDescription = page.locator('textarea[name="metadata-description"]');
107106
this.inputConstraintModel = page.locator(this.inputConstraintModelSelector);

src/components/constraints/ConstraintForm.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@
242242
<PageTitle subTitle={pageSubtitle} title={pageTitle} />
243243

244244
<AssociationForm
245-
allMetadata={$constraints || []}
245+
allMetadata={$constraints}
246246
defaultDefinitionCode={`export default (): Constraint => {\n\n}\n`}
247247
definitionTypeConfigurations={{
248248
code: { label: 'EDSL' },

src/components/constraints/Constraints.svelte

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,15 @@
9393
let hasPermission: boolean = false;
9494
let selectedConstraint: ConstraintMetadata | null = null;
9595
96-
$: filteredConstraints = ($constraints || []).filter(constraint => {
96+
$: filteredConstraints = $constraints.filter(constraint => {
9797
const filterTextLowerCase = filterText.toLowerCase();
9898
const includesId = `${constraint.id}`.includes(filterTextLowerCase);
9999
const includesName = constraint.name.toLocaleLowerCase().includes(filterTextLowerCase);
100100
return includesId || includesName;
101101
});
102102
$: hasPermission = featurePermissions.constraints.canCreate(user);
103103
$: if (selectedConstraint !== null) {
104-
const found = ($constraints || []).findIndex(constraint => constraint.id === selectedConstraint?.id);
104+
const found = $constraints.findIndex(constraint => constraint.id === selectedConstraint?.id);
105105
if (found === -1) {
106106
selectedConstraint = null;
107107
}
@@ -165,14 +165,14 @@
165165
166166
function deleteConstraintContext(event: CustomEvent<RowId[]>) {
167167
const id = event.detail[0] as number;
168-
const constraint = ($constraints || []).find(c => c.id === id);
168+
const constraint = $constraints.find(c => c.id === id);
169169
if (constraint) {
170170
deleteConstraint(constraint);
171171
}
172172
}
173173
174174
function editConstraint({ id }: Pick<ConstraintMetadata, 'id'>) {
175-
const constraint = ($constraints || []).find(c => c.id === id);
175+
const constraint = $constraints.find(c => c.id === id);
176176
goto(`${base}/constraints/edit/${id}?${SearchParameters.REVISION}=${constraint?.versions[0].revision}`);
177177
}
178178
@@ -235,24 +235,21 @@
235235
</svelte:fragment>
236236

237237
<svelte:fragment slot="body">
238-
{#if $initialConstraintsLoading || filteredConstraints.length}
239-
<SingleActionDataGrid
240-
showLoadingSkeleton
241-
loading={$initialConstraintsLoading}
242-
{columnDefs}
243-
hasEdit={true}
244-
{hasDeletePermission}
245-
{hasEditPermission}
246-
itemDisplayText="Constraint"
247-
items={filteredConstraints}
248-
{user}
249-
on:deleteItem={deleteConstraintContext}
250-
on:editItem={editConstraintContext}
251-
on:rowSelected={toggleConstraint}
252-
/>
253-
{:else}
254-
<div class="p1 st-typography-label">No Constraints Found</div>
255-
{/if}
238+
<SingleActionDataGrid
239+
showLoadingSkeleton
240+
loading={$initialConstraintsLoading}
241+
{columnDefs}
242+
hasEdit={true}
243+
{hasDeletePermission}
244+
{hasEditPermission}
245+
itemDisplayText="Constraint"
246+
noRowsOverlayText="No Constraints Found"
247+
items={filteredConstraints}
248+
{user}
249+
on:deleteItem={deleteConstraintContext}
250+
on:editItem={editConstraintContext}
251+
on:rowSelected={toggleConstraint}
252+
/>
256253
</svelte:fragment>
257254
</Panel>
258255

src/components/constraints/ConstraintsPanel.svelte

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
constraintResponseMap,
2020
constraintResponses,
2121
constraintVisibilityMap,
22+
constraints,
2223
constraintsMap,
2324
constraintsStatus,
2425
getConstraintDefaultsKey,
@@ -51,14 +52,17 @@
5152
import { required } from '../../utilities/validators';
5253
import CollapsibleListControls from '../CollapsibleListControls.svelte';
5354
import DatePickerField from '../form/DatePickerField.svelte';
54-
import Loading from '../Loading.svelte';
5555
import GridMenu from '../menus/GridMenu.svelte';
56+
import AsyncContentState from '../ui/AsyncContentState.svelte';
5657
import DatePickerActionButton from '../ui/DatePicker/DatePickerActionButton.svelte';
5758
import Panel from '../ui/Panel.svelte';
5859
import PanelHeaderActionButton from '../ui/PanelHeaderActionButton.svelte';
5960
import PanelHeaderActions from '../ui/PanelHeaderActions.svelte';
6061
import ConstraintListItem from './ConstraintListItem.svelte';
6162
63+
const constraintPlanSpecsError = constraintPlanSpecs.error;
64+
const constraintsError = constraints.error;
65+
6266
export let gridSection: ViewGridSection;
6367
export let user: User | null;
6468
@@ -138,7 +142,7 @@
138142
filterText,
139143
showConstraintsWithNoViolations,
140144
);
141-
$: numOfPrivateConstraints = ($constraintPlanSpecs || []).length - $allowedConstraintPlanSpecs.length;
145+
$: numOfPrivateConstraints = $constraintPlanSpecs.length - $allowedConstraintPlanSpecs.length;
142146
143147
// Fetch effective arguments for JAR type constraints when specs and metadata are available
144148
// Need to depend on both $allowedConstraintPlanSpecs and $constraintsMap to avoid race condition
@@ -498,21 +502,29 @@
498502
</CollapsibleListControls>
499503

500504
<div class="pt-2">
501-
{#if $initialConstraintsLoading || $initialConstraintPlanSpecsLoading}
502-
<div class="p-1">
503-
<Loading />
504-
</div>
505-
{:else if !filteredConstraintPlanSpecifications.length}
506-
<div class="st-typography-label filter-label-row pt-1">
507-
<div class="filter-label">No constraints found</div>
508-
<div class="private-label">
509-
{#if numOfPrivateConstraints > 0}
510-
{numOfPrivateConstraints} constraint{numOfPrivateConstraints !== 1 ? 's' : ''}
511-
{numOfPrivateConstraints > 1 ? 'are' : 'is'} private and not shown
512-
{/if}
505+
<AsyncContentState
506+
loading={$initialConstraintsLoading || $initialConstraintPlanSpecsLoading}
507+
error={$constraintsError || $constraintPlanSpecsError || null}
508+
errorMessage="Failed to load constraints"
509+
showRetry
510+
empty={!filteredConstraintPlanSpecifications.length}
511+
on:retry={() => {
512+
constraints.restartSocket();
513+
constraintPlanSpecs.restartSocket();
514+
}}
515+
>
516+
<svelte:fragment slot="empty">
517+
<div class="st-typography-label filter-label-row pt-1">
518+
<div class="filter-label">No constraints found</div>
519+
<div class="private-label">
520+
{#if numOfPrivateConstraints > 0}
521+
{numOfPrivateConstraints} constraint{numOfPrivateConstraints !== 1 ? 's' : ''}
522+
{numOfPrivateConstraints > 1 ? 'are' : 'is'} private and not shown
523+
{/if}
524+
</div>
513525
</div>
514-
</div>
515-
{:else}
526+
</svelte:fragment>
527+
516528
<div class="st-typography-label filter-label-row pt-1">
517529
<div class="filter-label">
518530
{#if $cachedConstraintsStatus}
@@ -572,7 +584,7 @@
572584
/>
573585
{/if}
574586
{/each}
575-
{/if}
587+
</AsyncContentState>
576588
</div>
577589
</svelte:fragment>
578590
</Panel>

src/components/expansion/ExpansionPanel.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
hasCreatePermissionSequenceFilter = featurePermissions.sequenceFilter.canCreate(user);
6565
}
6666
67-
$: relevantSimulationDatasetIds = $simulationDatasetsPlan?.map(dataset => dataset.id);
67+
$: relevantSimulationDatasetIds = $simulationDatasetsPlan.map(dataset => dataset.id);
6868
6969
$: relevantExpansionSequences = $expansionSequences.filter(sequence =>
7070
relevantSimulationDatasetIds?.includes(sequence.simulation_dataset_id),

src/components/expansion/ExpansionRules.svelte

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
};
3232
type ExpansionRuleCellRendererParams = ICellRendererParams<ExpansionRule> & CellRendererParams;
3333
34+
const expansionRulesLoading = expansionRules.loading;
3435
const baseColumnDefs: DataGridColumnDef[] = [
3536
{
3637
field: 'id',
@@ -234,22 +235,21 @@
234235
</svelte:fragment>
235236

236237
<svelte:fragment slot="body">
237-
{#if filteredRules.length}
238-
<SingleActionDataGrid
239-
{columnDefs}
240-
hasEdit={true}
241-
{hasDeletePermission}
242-
{hasEditPermission}
243-
itemDisplayText="Rule"
244-
items={filteredRules}
245-
{user}
246-
on:deleteItem={deleteRuleContext}
247-
on:editItem={editRuleContext}
248-
on:rowSelected={toggleRule}
249-
/>
250-
{:else}
251-
<div class="st-typography-label">No Rules Found</div>
252-
{/if}
238+
<SingleActionDataGrid
239+
showLoadingSkeleton
240+
loading={$expansionRulesLoading}
241+
{columnDefs}
242+
hasEdit={true}
243+
{hasDeletePermission}
244+
{hasEditPermission}
245+
itemDisplayText="Rule"
246+
noRowsOverlayText="No Rules Found"
247+
items={filteredRules}
248+
{user}
249+
on:deleteItem={deleteRuleContext}
250+
on:editItem={editRuleContext}
251+
on:rowSelected={toggleRule}
252+
/>
253253
</svelte:fragment>
254254
</Panel>
255255

src/components/expansion/ExpansionRuns.svelte

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,14 @@
5656
5757
$: convertOutputToSequence(selectedSequence);
5858
$: if (parcel) {
59-
const unparsedChannelDictionary = $channelDictionaries?.find(
59+
const unparsedChannelDictionary = $channelDictionaries.find(
6060
channelDictionaryMetadata => channelDictionaryMetadata.id === parcel?.channel_dictionary_id,
6161
);
62-
const unparsedCommandDictionary = $commandDictionaries?.find(
62+
const unparsedCommandDictionary = $commandDictionaries.find(
6363
commandDictionaryMetadata => commandDictionaryMetadata.id === parcel?.command_dictionary_id,
6464
);
65-
const unparsedParameterDictionaries = ($parameterDictionariesStore || []).filter(parameterDictionaryMetadata => {
66-
const parameterDictionary = $parcelToParameterDictionaries?.find(
65+
const unparsedParameterDictionaries = $parameterDictionariesStore.filter(parameterDictionaryMetadata => {
66+
const parameterDictionary = $parcelToParameterDictionaries.find(
6767
parcelToParameterDictionary =>
6868
parcelToParameterDictionary.parameter_dictionary_id === parameterDictionaryMetadata.id &&
6969
parcelToParameterDictionary.parcel_id === parcel?.id,

src/components/expansion/ExpansionSets.svelte

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
editRule: (expansionRule: ExpansionRuleSlim) => void;
3030
};
3131
32+
const expansionSetsLoading = expansionSets.loading;
33+
3234
const baseExpansionSetColumnDefs: DataGridColumnDef[] = [
3335
{
3436
field: 'id',
@@ -221,19 +223,18 @@
221223
</svelte:fragment>
222224

223225
<svelte:fragment slot="body">
224-
{#if $expansionSets.length}
225-
<SingleActionDataGrid
226-
columnDefs={expansionSetColumnDefs}
227-
hasDeletePermission={hasDeleteExpansionSetPermission}
228-
itemDisplayText="Expansion Set"
229-
items={$expansionSets}
230-
{user}
231-
on:deleteItem={deleteSetContext}
232-
on:rowSelected={toggleSet}
233-
/>
234-
{:else}
235-
No Expansion Sets Found
236-
{/if}
226+
<SingleActionDataGrid
227+
showLoadingSkeleton
228+
loading={$expansionSetsLoading}
229+
columnDefs={expansionSetColumnDefs}
230+
hasDeletePermission={hasDeleteExpansionSetPermission}
231+
itemDisplayText="Expansion Set"
232+
noRowsOverlayText="No Expansion Sets Found"
233+
items={$expansionSets}
234+
{user}
235+
on:deleteItem={deleteSetContext}
236+
on:rowSelected={toggleSet}
237+
/>
237238
</svelte:fragment>
238239
</Panel>
239240

src/components/external-source/ExternalSourceManager.svelte

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
};
6767
type SourceCellRendererParams = ICellRendererParams<ExternalSourceSlim> & CellRendererParams;
6868
69+
const externalSourcesLoading = externalSources.loading;
70+
6971
// Permissions
7072
const deletePermissionError = 'You do not have permission to delete an external source.';
7173
const createPermissionError = 'You do not have permission to create an external source.';
@@ -522,7 +524,7 @@
522524
{#each selectedSourceLinkedDerivationGroupsPlans as linkedPlanDerivationGroup}
523525
<div class="st-typography-body collapse-important-text">
524526
<a href="{base}/plans/{linkedPlanDerivationGroup.plan_id}">
525-
{($plans || []).find(plan => {
527+
{$plans.find(plan => {
526528
return linkedPlanDerivationGroup.plan_id === plan.id;
527529
})?.name}
528530
</a>
@@ -648,26 +650,24 @@
648650
</slot>
649651
</svelte:fragment>
650652
<svelte:fragment slot="body">
651-
{#if $externalSources.length}
652-
<div id="external-sources-table" style:height="100%">
653-
<BulkActionDataGrid
654-
bind:dataGrid
655-
{columnDefs}
656-
hasDeletePermission={hasDeleteExternalSourcePermissionOnRow}
657-
singleItemDisplayText="External Source"
658-
pluralItemDisplayText="External Sources"
659-
{filterExpression}
660-
items={$externalSources}
661-
{user}
662-
getRowId={getExternalSourceSlimRowId}
663-
on:rowClicked={({ detail }) => selectSource(detail.data)}
664-
on:bulkDeleteItems={({ detail }) => onDeleteExternalSource(detail)}
665-
bind:selectedItemId={selectedSourceId}
666-
/>
667-
</div>
668-
{:else}
669-
<p>No external sources matching the selected external source type(s).</p>
670-
{/if}
653+
<div id="external-sources-table" style:height="100%">
654+
<BulkActionDataGrid
655+
bind:dataGrid
656+
{columnDefs}
657+
hasDeletePermission={hasDeleteExternalSourcePermissionOnRow}
658+
singleItemDisplayText="External Source"
659+
pluralItemDisplayText="External Sources"
660+
{filterExpression}
661+
items={$externalSources}
662+
loading={$externalSourcesLoading}
663+
noRowsOverlayText="No External Sources Found"
664+
{user}
665+
getRowId={getExternalSourceSlimRowId}
666+
on:rowClicked={({ detail }) => selectSource(detail.data)}
667+
on:bulkDeleteItems={({ detail }) => onDeleteExternalSource(detail)}
668+
bind:selectedItemId={selectedSourceId}
669+
/>
670+
</div>
671671
</svelte:fragment>
672672
</Panel>
673673

0 commit comments

Comments
 (0)