Skip to content

Commit cf5041d

Browse files
authored
[ENG-8505] Add the ability to select a folder with oauth working (CenterForOpenScience#316)
* feat(oauth): oauth is working for gfp * chore(test): removed a pointless test * feat(ENG-8505): added the ability to select a folder with tests * feat(connect): added the gfp to the connect flow
1 parent 0c81f6d commit cf5041d

18 files changed

+176
-116
lines changed

eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ module.exports = tseslint.config(
8282
],
8383
'simple-import-sort/exports': 'error',
8484
'unused-imports/no-unused-imports': 'error',
85+
'no-console': 'error',
8586
},
8687
},
8788
{

src/app/features/project/addons/addons.component.spec.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { AddonsComponent } from './addons.component';
1010

1111
import { OSFTestingModule } from '@testing/osf.testing.module';
1212

13-
describe('AddonsComponent', () => {
13+
describe('Component: Addons', () => {
1414
let component: AddonsComponent;
1515
let fixture: ComponentFixture<AddonsComponent>;
1616

@@ -33,10 +33,6 @@ describe('AddonsComponent', () => {
3333
fixture.detectChanges();
3434
});
3535

36-
it('should create', () => {
37-
expect(component).toBeTruthy();
38-
});
39-
4036
it('should render the connected description paragraph', () => {
4137
component['selectedTab'].set(component['AddonTabValue'].ALL_ADDONS);
4238
fixture.detectChanges();

src/app/features/project/addons/components/configure-addon/configure-addon.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ <h2 class="align-self-center">
6565
</div>
6666
<osf-folder-selector
6767
[isGoogleFilePicker]="isGoogleDrive()"
68+
[accountId]="addon()?.baseAccountId || ''"
6869
[accountName]="addon()?.displayName || ''"
6970
[operationInvocationResult]="operationInvocation()?.operationResult || []"
7071
[accountNameControl]="accountNameControl"

src/app/features/project/addons/components/connect-configured-addon/connect-configured-addon.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ <h2 class="pt-2">{{ 'settings.addons.connectAddon.chooseExistingAccount' | trans
114114
<section class="flex flex-column gap-5">
115115
<h2 class="pt-2">{{ 'settings.addons.connectAddon.configure' | translate }} {{ addon()?.displayName }}</h2>
116116
<osf-folder-selector
117-
[isGoogleFilePicker]="false"
117+
[isGoogleFilePicker]="isGoogleDrive()"
118+
[accountId]="chosenAccountId()"
118119
[accountName]="chosenAccountName()"
119120
[operationInvocationResult]="operationInvocation()?.operationResult || []"
120121
[accountNameControl]="accountNameControl"

src/app/features/project/addons/components/connect-configured-addon/connect-configured-addon.component.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ export class ConnectConfiguredAddonComponent {
8181
protected chosenAccountId = signal('');
8282
protected chosenAccountName = signal('');
8383
protected selectedRootFolderId = signal('');
84+
private selectedAccount = signal<AuthorizedAccountModel>({} as AuthorizedAccountModel);
85+
public readonly isGoogleDrive = computed(() => {
86+
return this.selectedAccount()?.externalServiceName === 'googledrive';
87+
});
8488

8589
protected addonsUserReference = select(AddonsSelectors.getAddonsUserReference);
8690
protected createdAuthorizedAddon = select(AddonsSelectors.getCreatedOrUpdatedAuthorizedAddon);
@@ -137,14 +141,15 @@ export class ConnectConfiguredAddonComponent {
137141

138142
protected handleCreateConfiguredAddon() {
139143
const addon = this.addon();
140-
const selectedAccount = this.currentAuthorizedAddonAccounts().find(
141-
(account) => account.id === this.chosenAccountId()
144+
this.selectedAccount.set(
145+
this.currentAuthorizedAddonAccounts().find((account) => account.id === this.chosenAccountId()) ||
146+
({} as AuthorizedAccountModel)
142147
);
143-
if (!addon || !selectedAccount) return;
148+
if (!addon || !this.selectedAccount()) return;
144149

145150
const payload = this.addonFormService.generateConfiguredAddonCreatePayload(
146151
addon,
147-
selectedAccount,
152+
this.selectedAccount(),
148153
this.userReferenceId(),
149154
this.resourceUri(),
150155
this.accountNameControl.value || '',
@@ -181,19 +186,20 @@ export class ConnectConfiguredAddonComponent {
181186
}
182187

183188
protected handleConfirmAccountConnection(): void {
184-
const selectedAccount = this.currentAuthorizedAddonAccounts().find(
185-
(account) => account.id === this.chosenAccountId()
189+
this.selectedAccount.set(
190+
this.currentAuthorizedAddonAccounts().find((account) => account.id === this.chosenAccountId()) ||
191+
({} as AuthorizedAccountModel)
186192
);
187193

188-
if (!selectedAccount) return;
194+
if (!this.selectedAccount()) return;
189195

190-
const dialogRef = this.addonDialogService.openConfirmAccountConnectionDialog(selectedAccount);
196+
const dialogRef = this.addonDialogService.openConfirmAccountConnectionDialog(this.selectedAccount());
191197

192198
dialogRef.subscribe((result) => {
193199
if (result?.success) {
194200
this.stepper()?.value.set(ProjectAddonsStepperValue.CONFIGURE_ROOT_FOLDER);
195-
this.chosenAccountName.set(selectedAccount.displayName);
196-
this.accountNameControl.setValue(selectedAccount.displayName);
201+
this.chosenAccountName.set(this.selectedAccount().displayName);
202+
this.accountNameControl.setValue(this.selectedAccount().displayName);
197203
}
198204
});
199205
}

src/app/shared/components/addons/folder-selector/folder-selector.component.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ <h3 class="mt-4 mb-2">
3434
@if (isOperationInvocationSubmitting()) {
3535
<p-skeleton width="100%" height="7.5rem" borderRadius="16" />
3636
} @else if (isGoogleFilePicker()) {
37-
<osf-google-file-picker [isFolderPicker]="true"></osf-google-file-picker>
37+
<osf-google-file-picker
38+
[isFolderPicker]="true"
39+
[accountId]="accountId()"
40+
[handleFolderSelection]="handleFolderSelection"
41+
[rootFolder]="selectedRootFolder()"
42+
></osf-google-file-picker>
3843
} @else {
3944
<div class="folders-table flex flex-column">
4045
<div class="folders-table-heading flex justify-content-between">

src/app/shared/components/addons/folder-selector/folder-selector.component.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
66
import { OperationNames } from '@osf/features/project/addons/enums';
77
import { FolderSelectorComponent } from '@shared/components/addons';
88
import { MOCK_STORE, TranslateServiceMock } from '@shared/mocks';
9-
import { StorageItem } from '@shared/models';
9+
import { StorageItemModel } from '@shared/models';
1010

1111
describe('FolderSelectorComponent', () => {
1212
let component: FolderSelectorComponent;
@@ -65,11 +65,11 @@ describe('FolderSelectorComponent', () => {
6565
});
6666

6767
it('should set selectedRootFolderId', () => {
68-
const mockFolder: StorageItem = {
68+
const mockFolder: StorageItemModel = {
6969
itemId: 'test-folder-id',
7070
itemName: 'Test Folder',
7171
itemType: 'folder',
72-
} as StorageItem;
72+
} as StorageItemModel;
7373

7474
(component as any).selectedRootFolder.set(mockFolder);
7575
(component as any).handleSave();

src/app/shared/components/addons/folder-selector/folder-selector.component.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
2727
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
2828

2929
import { OperationNames } from '@osf/features/project/addons/enums';
30-
import { OperationInvokeData, StorageItem } from '@shared/models';
30+
import { OperationInvokeData, StorageItemModel } from '@shared/models';
3131
import { AddonsSelectors } from '@shared/stores/addons';
3232

3333
import { GoogleFilePickerComponent } from './google-file-picker/google-file-picker.component';
@@ -56,7 +56,8 @@ export class FolderSelectorComponent implements OnInit {
5656

5757
isGoogleFilePicker = input.required<boolean>();
5858
accountName = input.required<string>();
59-
operationInvocationResult = input.required<StorageItem[]>();
59+
accountId = input.required<string>();
60+
operationInvocationResult = input.required<StorageItemModel[]>();
6061
accountNameControl = input(new FormControl());
6162
isCreateMode = input(false);
6263

@@ -67,7 +68,7 @@ export class FolderSelectorComponent implements OnInit {
6768
protected readonly OperationNames = OperationNames;
6869
protected hasInputChanged = signal(false);
6970
protected hasFolderChanged = signal(false);
70-
protected selectedRootFolder = signal<StorageItem | null>(null);
71+
public selectedRootFolder = signal<StorageItemModel | null>(null);
7172
protected breadcrumbItems = signal<MenuItem[]>([]);
7273
protected initiallySelectedFolder = select(AddonsSelectors.getSelectedFolder);
7374
protected isOperationInvocationSubmitting = select(AddonsSelectors.getOperationInvocationSubmitting);
@@ -126,10 +127,10 @@ export class FolderSelectorComponent implements OnInit {
126127
this.cancelSelection.emit();
127128
}
128129

129-
protected handleFolderSelection(folder: StorageItem): void {
130+
public handleFolderSelection = (folder: StorageItemModel): void => {
130131
this.selectedRootFolder.set(folder);
131132
this.hasFolderChanged.set(folder?.itemId !== this.initiallySelectedFolder()?.itemId);
132-
}
133+
};
133134

134135
private updateBreadcrumbs(
135136
operationName: OperationNames,

src/app/shared/components/addons/folder-selector/google-file-picker/google-file-picker.component.spec.ts

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ describe('Component: Google File Picker', () => {
1717
loadGapiModules: jest.fn().mockReturnValue(of(void 0)),
1818
};
1919

20+
const handleFolderSelection = jest.fn();
2021
const setDeveloperKey = jest.fn().mockReturnThis();
2122
const setAppId = jest.fn().mockReturnThis();
2223
const addView = jest.fn().mockReturnThis();
@@ -39,6 +40,18 @@ describe('Component: Google File Picker', () => {
3940
selectSnapshot: jest.fn().mockReturnValue('mock-token'),
4041
};
4142

43+
beforeAll(() => {
44+
window.google = {
45+
picker: {
46+
Action: null,
47+
},
48+
};
49+
});
50+
51+
afterAll(() => {
52+
delete (window as any).google;
53+
});
54+
4255
describe('isFolderPicker - true', () => {
4356
beforeEach(async () => {
4457
jest.clearAllMocks();
@@ -84,8 +97,10 @@ describe('Component: Google File Picker', () => {
8497
fixture = TestBed.createComponent(GoogleFilePickerComponent);
8598
component = fixture.componentInstance;
8699
fixture.componentRef.setInput('isFolderPicker', true);
87-
fixture.componentRef.setInput('rootFolderId', 'root-folder-id');
88-
fixture.componentRef.setInput('selectedFolderName', 'selected-folder-name');
100+
fixture.componentRef.setInput('rootFolder', {
101+
itemId: 'root-folder-id',
102+
});
103+
fixture.componentRef.setInput('handleFolderSelection', handleFolderSelection);
89104
fixture.componentRef.setInput('accountId', 'account-id');
90105
fixture.detectChanges();
91106
});
@@ -118,6 +133,41 @@ describe('Component: Google File Picker', () => {
118133
expect(build).toHaveBeenCalledWith();
119134
expect(setVisible).toHaveBeenCalledWith(true);
120135
});
136+
137+
describe('pickerCallback', () => {
138+
it('should handle a folder selection `PICKED` action', () => {
139+
window.google.picker.Action = {
140+
PICKED: 'PICKED',
141+
};
142+
component.pickerCallback(
143+
Object({
144+
action: 'PICKED',
145+
docs: [
146+
Object({
147+
itemId: 'item id',
148+
itemName: 'item name',
149+
}),
150+
],
151+
})
152+
);
153+
154+
expect(handleFolderSelection).toHaveBeenCalledWith(Object({}));
155+
});
156+
157+
it('should handle a folder selection not `PICKED` action', () => {
158+
window.google.picker.Action = {
159+
PICKED: 'not picked',
160+
};
161+
162+
component.pickerCallback(
163+
Object({
164+
action: 'Loading',
165+
})
166+
);
167+
168+
expect(handleFolderSelection).not.toHaveBeenCalled();
169+
});
170+
});
121171
});
122172

123173
describe('isFolderPicker - false', () => {
@@ -164,8 +214,10 @@ describe('Component: Google File Picker', () => {
164214
fixture = TestBed.createComponent(GoogleFilePickerComponent);
165215
component = fixture.componentInstance;
166216
fixture.componentRef.setInput('isFolderPicker', false);
167-
fixture.componentRef.setInput('rootFolderId', 'root-folder-id');
168-
fixture.componentRef.setInput('selectedFolderName', 'selected-folder-name');
217+
fixture.componentRef.setInput('rootFolder', {
218+
itemId: 'root-folder-id',
219+
});
220+
fixture.componentRef.setInput('handleFolderSelection', jest.fn());
169221
fixture.detectChanges();
170222
});
171223

0 commit comments

Comments
 (0)