Skip to content

Commit

Permalink
feat(storage-browser): integrate browser navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
calebpollman committed Oct 24, 2024
1 parent 1169805 commit 7df9f79
Show file tree
Hide file tree
Showing 109 changed files with 1,002 additions and 1,896 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Button, Flex } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react-storage/storage-browser-styles.css';
import '@aws-amplify/ui-react-storage/styles.css';

const { StorageBrowser, useControl } = createStorageBrowser({
const { StorageBrowser } = createStorageBrowser({
config: managedAuthAdapter,
});

Expand All @@ -22,17 +22,21 @@ function LocationActionView() {
}

function MyStorageBrowser() {
const [{ selected }] = useControl('LOCATION_ACTIONS');
const [type, setActionType] = React.useState<string | undefined>(undefined);

return (
<Flex>
<Flex direction={'column'}>
<StorageBrowser.LocationsView />
</Flex>
<Flex minWidth={'50vw'} direction={'column'}>
<StorageBrowser.LocationDetailView />
<StorageBrowser.LocationDetailView
onActionSelect={(actionType) => {
setActionType(actionType);
}}
/>
</Flex>
{selected.type ? <LocationActionView /> : null}
{type ? <LocationActionView /> : null}
</Flex>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';

import { ElementsProvider } from '@aws-amplify/ui-react-core/elements';

import { ComposablesProvider } from './composables/context';
import { ComposableTypes as Composables } from './composables/types';
import { StorageBrowserElements } from './context/elements';

export interface ComponentsProviderProps {
children?: React.ReactNode;
// temp disablng of linting rule to note where `components`/"slots" context is provided
// eslint-disable-next-line react/no-unused-prop-types
components?: Composables;
elements?: Partial<StorageBrowserElements>;
}

export function ComponentsProvider(
props: ComponentsProviderProps
): React.JSX.Element {
const { children, elements } = props;

return (
<ElementsProvider elements={elements}>
<ComposablesProvider>{children}</ComposablesProvider>
</ElementsProvider>
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

import { useViews } from './views';
import { useControl } from './context/control';
import { useStore } from './providers/store';

/**
* Handles default `StorageBrowser` behavior:
Expand All @@ -12,16 +12,14 @@ import { useControl } from './context/control';
export function StorageBrowserDefault(): React.JSX.Element {
const { LocationActionView, LocationDetailView, LocationsView } = useViews();

const [{ location }] = useControl('NAVIGATE');
const [{ selected }] = useControl('LOCATION_ACTIONS');
const [{ actionType, history }] = useStore();
const { current } = history;

const { type } = selected;

if (type) {
if (actionType) {
return <LocationActionView />;
}

if (location) {
if (current) {
return <LocationDetailView />;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';

import * as ActionsModule from '../context/actions';
import * as ActionsModule from '../do-not-import-from-here/actions';
import * as ControlsModule from '../context/control';
import { ViewsProvider } from '../views/context';
import { StorageBrowserDefault } from '../StorageBrowserDefault';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';

import * as ActionsModule from '../context/actions';
import * as ActionsModule from '../do-not-import-from-here/actions';
import * as ControlsModule from '../context/control';

import { createStorageBrowser } from '../createStorageBrowser';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@ export interface ActionConfigsProviderProps {

const defaultValue: ActionConfigs = {};

export interface ActionConfigsProviderProps {
actions?: ActionConfigs;
children?: React.ReactNode;
}
export const { useActionConfigs, ActionConfigsProvider } =
createContextUtilities({ contextName: 'ActionConfigs', defaultValue });
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IconVariant } from '../../context/elements';
import {
ListLocationsHandler,
ListLocationItemsHandler,
LocationItem,
LocationItemData,
LocationItemType,
UploadHandler,
CreateFolderHandler,
Expand Down Expand Up @@ -33,7 +33,7 @@ export interface ActionListItemConfig {
* conditionally disable item selection based on currently selected values
* @default false
*/
disable?: (selectedValues: LocationItem[] | undefined) => boolean;
disable?: (selectedValues: LocationItemData[] | undefined) => boolean;

/**
* open native OS file picker with associated selection type on item select
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as StorageModule from 'aws-amplify/storage';

import { resolveHandlerResult } from '../utils';
import { LocationAccess, LocationData } from '../types';

import { parseLocationAccess, resolveHandlerResult } from '../utils';

const isCancelErrorSpy = jest.spyOn(StorageModule, 'isCancelError');

Expand Down Expand Up @@ -83,3 +85,94 @@ describe('resolveHandlerResult', () => {
expect(onCancel).toHaveBeenCalledWith(key);
});
});

describe('parseLocationAccess', () => {
const bucket = 'test-bucket';
const folderPrefix = 'test-prefix/';
const filePath = 'some-file.jpeg2000';

const id = 'intentionally-static-test-id';
beforeAll(() => {
Object.defineProperty(globalThis, 'crypto', {
value: { randomUUID: () => id },
});
});

it('throws if provided an invalid location scope', () => {
const invalidLocation: LocationAccess = {
scope: 'nope',
permission: 'READ',
type: 'BUCKET',
};

expect(() => parseLocationAccess(invalidLocation)).toThrow(
'Invalid scope: nope'
);
});

it('throws if provided an invalid location type', () => {
const invalidLocation: LocationAccess = {
scope: 's3://yes',
permission: 'READ',
// @ts-expect-error intentional coercing to allow unhappy path test
type: 'NOT_BUCKET',
};

expect(() => parseLocationAccess(invalidLocation)).toThrow(
'Invalid location type: NOT_BUCKET'
);
});

it('parses a BUCKET location as expected', () => {
const location: LocationAccess = {
permission: 'WRITE',
scope: `s3://${bucket}/*`,
type: 'BUCKET',
};
const expected: LocationData = {
bucket,
id,
prefix: '',
permission: 'WRITE',
type: 'BUCKET',
};

expect(parseLocationAccess(location)).toStrictEqual(expected);
});

it('parses a PREFIX location as expected', () => {
const location: LocationAccess = {
permission: 'WRITE',
scope: `s3://${bucket}/${folderPrefix}*`,
type: 'PREFIX',
};

const expected: LocationData = {
bucket,
id,
prefix: folderPrefix,
permission: 'WRITE',
type: 'PREFIX',
};

expect(parseLocationAccess(location)).toStrictEqual(expected);
});

it('parses an OBJECT location as expected', () => {
const location: LocationAccess = {
permission: 'WRITE',
scope: `s3://${bucket}/${folderPrefix}${filePath}`,
type: 'OBJECT',
};

const expected: LocationData = {
bucket,
id,
prefix: `${folderPrefix}${filePath}`,
permission: 'WRITE',
type: 'OBJECT',
};

expect(parseLocationAccess(location)).toStrictEqual(expected);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ export interface CopyHandlerOutput extends TaskHandlerOutput {}
export interface CopyHandler
extends TaskHandler<CopyHandlerInput, CopyHandlerOutput> {}

export const copyHandler: CopyHandler = ({ config, options, prefix, data }) => {
export const copyHandler: CopyHandler = (input) => {
const { config, key, options, prefix, data } = input;
const { accountId, credentials } = config;
const { payload, key } = data;
const { payload } = data;
const { destinationPrefix } = payload;

const sourceKey = `${prefix}${key}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface DeleteHandler

export const deleteHandler: DeleteHandler = ({
config,
data: { key },
key,
prefix,
options,
}): DeleteHandlerOutput => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export * from './download';
export * from './listLocationItems';
export * from './listLocations';
export * from './upload';

export * from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,23 @@ import {
ListHandlerOutput,
} from '../types';

/**
* handler types
*/
export interface FolderItem {
export interface FolderData {
key: string;
id: string;
type: 'FOLDER';
}

export interface FileItem {
export interface FileData {
key: string;
data?: File;
lastModified: Date;
id: string;
size: number;
type: 'FILE';
}

export type LocationItem = FileItem | FolderItem;
export type LocationItemData = FileData | FolderData;

export type LocationItemType = LocationItem['type'];
export type LocationItemType = LocationItemData['type'];

export interface ListLocationItemsHandlerOptions
extends ListHandlerOptions<LocationItemType> {
Expand All @@ -35,7 +33,7 @@ export interface ListLocationItemsHandlerInput
extends ListHandlerInput<ListLocationItemsHandlerOptions> {}

export interface ListLocationItemsHandlerOutput
extends ListHandlerOutput<LocationItem> {}
extends ListHandlerOutput<LocationItemData> {}

export interface ListLocationItemsHandler
extends ListHandler<
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import { Permission } from '../../storage-internal';

import { ListHandlerOptions, ListHandlerOutput, ListHandler } from '../types';

export type LocationType = 'OBJECT' | 'PREFIX' | 'BUCKET';

export interface LocationData {
bucket: string;
permission: Permission;
prefix: string;
type: LocationType;
}
import { LocationData, LocationType } from './types';

type ExcludeType = Permission | LocationType;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ListLocations, Permission } from '../../storage-internal';

export type LocationAccess = Awaited<
ReturnType<ListLocations>
>['locations'][number];

export type LocationType = 'OBJECT' | 'PREFIX' | 'BUCKET';

export interface LocationData {
bucket: string;
id: string;
permission: Permission;
prefix: string;
type: LocationType;
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ export const UNDEFINED_CALLBACKS = {
export const uploadHandler: UploadHandler = ({
config: _config,
data: _data,
key,
options: _options,
prefix,
}) => {
const { accountId, credentials, ...config } = _config;
const { key, payload: data } = _data;
const { payload: data } = _data;
const { onProgress, preventOverwrite, ...options } = _options ?? {};

const bucket = constructBucket(config);
Expand Down
Loading

0 comments on commit 7df9f79

Please sign in to comment.