Skip to content

Commit

Permalink
SF-2818 Move instructions out of placeholder for open resource tab (#…
Browse files Browse the repository at this point in the history
…2806)

The lengthy placeholder text was getting cut off on mobile viewports.
This moves the instructive placeholder text above the input box, and
uses a smaller placeholder text.

A Storybook has been introduced for the dialog as it has a number of
states and messages.
  • Loading branch information
marksvc authored Oct 16, 2024
1 parent 071f399 commit 885002d
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
<ng-container *transloco="let t; read: 'editor_add_tab_resource_dialog'">
<h1 mat-dialog-title>{{ t("dialog_title") }}</h1>
<mat-dialog-content>
<div>{{ isLoading ? t("loading_ellipsis") : t("select_project_or_resource") }}</div>
<div [formGroup]="form">
<app-project-select
formControlName="sourceParatextId"
[placeholder]="isLoading ? t('placeholder_loading') : t('placeholder_ready')"
[placeholder]="t('project_or_resource')"
[projects]="projects"
[resources]="resources"
[hiddenParatextIds]="dialogData.excludedParatextIds"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
mat-dialog-content {
display: flex;
flex-direction: column;
gap: 1em;
}

mat-progress-bar,
app-sync-progress {
position: absolute;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { provideAnimations } from '@angular/platform-browser/animations';
import { Meta, moduleMetadata } from '@storybook/angular';
import { of } from 'rxjs';
import { instance, mock, when } from 'ts-mockito';
import { I18nStoryModule } from 'xforge-common/i18n-story.module';
import { OnlineStatusService } from 'xforge-common/online-status.service';
import { XForgeCommonModule } from 'xforge-common/xforge-common.module';
import {
MatDialogLaunchComponent,
MatDialogStoryConfig,
matDialogStory
} from '../../../../../../.storybook/util/mat-dialog-launch';
import { ParatextProject } from '../../../../core/models/paratext-project';
import { ParatextService } from '../../../../core/paratext.service';
import { PermissionsService } from '../../../../core/permissions.service';
import { SFProjectService } from '../../../../core/sf-project.service';
import {
EditorTabAddResourceDialogComponent,
EditorTabAddResourceDialogData
} from './editor-tab-add-resource-dialog.component';
import { EditorTabAddResourceDialogService } from './editor-tab-add-resource-dialog.service';

const mockEditorTabAddResourceDialogService = mock(EditorTabAddResourceDialogService);
const mockSFProjectService = mock(SFProjectService);
const mockParatextService = mock(ParatextService);
const mockMatDialogRef = mock(MatDialogRef);
const mockPermissionsService = mock(PermissionsService);
const mockOnlineStatusService = mock(OnlineStatusService);

/** See also editor-tab-add-resource-dialog.component.spec.ts createTestParatextProject(). */
function createTestParatextProject(index: number, overrides?: Partial<ParatextProject>): ParatextProject {
return {
paratextId: `ptId${index}`,
name: `Paratext Project ${index}`,
shortName: `PTProject${index}`,
languageTag: 'en',
projectId: `projectId${index}`,
isConnectable: true,
isConnected: false,
...overrides
};
}

// Different situations that the story might happen in.
interface StoryAppState {
online: boolean;
stillFetchingProjects: boolean;
undefinedProjectsAndResources: boolean;
errorLoadingProjectList: boolean;
data: EditorTabAddResourceDialogData;
}

// Default values for story situations.
const defaultArgs: StoryAppState = {
online: true,
stillFetchingProjects: false,
undefinedProjectsAndResources: false,
errorLoadingProjectList: false,
data: { excludedParatextIds: [] }
};

const meta: Meta = {
title: 'Translate/Editor/Tabs/Add resource dialog',
component: MatDialogLaunchComponent,
decorators: [
moduleMetadata({}),
(story, context) => {
// Apply selected situations to a story.

when(mockOnlineStatusService.onlineStatus$).thenReturn(of(context.args.online));
when(mockOnlineStatusService.onlineBrowserStatus$).thenReturn(of(context.args.online));
when(mockOnlineStatusService.isOnline).thenReturn(context.args.online);
when(mockOnlineStatusService.isBrowserOnline).thenReturn(context.args.online);
when(mockOnlineStatusService.online).thenReturn(
new Promise(resolve => {
if (context.args.online) resolve();
// Else, never resolve.
})
);

let projects: ParatextProject[] | undefined = [createTestParatextProject(1), createTestParatextProject(2)];
let resources: ParatextProject[] | undefined = [createTestParatextProject(3), createTestParatextProject(4)];
if (context.args.undefinedProjectsAndResources === true) {
projects = undefined;
resources = undefined;
}

when(mockEditorTabAddResourceDialogService.getProjects()).thenReturn(Promise.resolve(projects));
when(mockEditorTabAddResourceDialogService.getResources()).thenReturn(Promise.resolve(resources));

if (context.args.stillFetchingProjects) {
when(mockEditorTabAddResourceDialogService.getProjects()).thenReturn(
new Promise((_resolve, _reject) => {
// Never resolve.
})
);
}

if (context.args.errorLoadingProjectList || context.args.online === false) {
when(mockEditorTabAddResourceDialogService.getProjects()).thenReject(new Error('Problem'));
when(mockEditorTabAddResourceDialogService.getResources()).thenReject(new Error('Problem'));
}

return story();
}
],
parameters: {
controls: {
expanded: true,
include: Object.keys(defaultArgs)
},
viewport: { defaultViewport: 'mobile1' }
},
args: defaultArgs
};
export default meta;

const dialogStoryConfig: MatDialogStoryConfig = {
imports: [XForgeCommonModule, I18nStoryModule],
providers: [
provideAnimations(),
{ provide: EditorTabAddResourceDialogService, useValue: instance(mockEditorTabAddResourceDialogService) },
{ provide: SFProjectService, useValue: instance(mockSFProjectService) },
{ provide: ParatextService, useValue: instance(mockParatextService) },
{ provide: PermissionsService, useValue: instance(mockPermissionsService) },
{ provide: MatDialogRef, useValue: instance(mockMatDialogRef) },
{ provide: OnlineStatusService, useValue: instance(mockOnlineStatusService) },
{ provide: MAT_DIALOG_DATA, useValue: {} }
],
declarations: [EditorTabAddResourceDialogComponent],
standaloneComponent: true
};

// List of stories.

// The dialog opens for the user and successfully fetches projects and resources.
export const Loaded = matDialogStory(EditorTabAddResourceDialogComponent, dialogStoryConfig);
Loaded.args = {};

// The dialog opens for the user and has not yet finished fetching projects or resources.
export const Loading = matDialogStory(EditorTabAddResourceDialogComponent, dialogStoryConfig);
Loading.args = { stillFetchingProjects: true };

// The project and resource lists might be given to us as undefined.
export const LoadedUndefinedProjectsAndResources = matDialogStory(
EditorTabAddResourceDialogComponent,
dialogStoryConfig
);
LoadedUndefinedProjectsAndResources.args = { undefinedProjectsAndResources: true };

// The dialog opens for the user and has an error when fetching the project and resource lists.
export const ErrorLoadingProjectListOnline = matDialogStory(EditorTabAddResourceDialogComponent, dialogStoryConfig);
ErrorLoadingProjectListOnline.args = { errorLoadingProjectList: true };

// The dialog opens for the user and has an error when fetching the project and resource lists. Possibly because the
// user is offline.
export const ErrorLoadingProjectListOffline = matDialogStory(EditorTabAddResourceDialogComponent, dialogStoryConfig);
ErrorLoadingProjectListOffline.args = { online: false, errorLoadingProjectList: true };
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,9 @@
"error_loading_resource": "There was an error loading the selected resource.",
"error_offline": "You are offline. Please connect to the internet to load this resource.",
"loading": "Loading",
"placeholder_loading": "{{ editor_add_tab_resource_dialog.loading }}...",
"placeholder_ready": "Select paratext project or DBL resource",
"loading_ellipsis": "{{ editor_add_tab_resource_dialog.loading }} ...",
"select_project_or_resource": "Select a Paratext project or DBL resource.",
"project_or_resource": "Project or resource",
"cancel": "Cancel",
"select": "Select"
},
Expand Down

0 comments on commit 885002d

Please sign in to comment.