Skip to content

Commit

Permalink
Media Utils: Add TypeScript support and export more utils (#64784)
Browse files Browse the repository at this point in the history
Co-authored-by: Nik Tsekouras <ntsekouras@outlook.com>
  • Loading branch information
swissspidy and ntsekouras committed Sep 5, 2024
1 parent 8b452c2 commit f4cad96
Show file tree
Hide file tree
Showing 27 changed files with 1,074 additions and 313 deletions.
1 change: 1 addition & 0 deletions packages/editor/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
{ "path": "../i18n" },
{ "path": "../icons" },
{ "path": "../keycodes" },
{ "path": "../media-utils" },
{ "path": "../notices" },
{ "path": "../plugins" },
{ "path": "../private-apis" },
Expand Down
4 changes: 4 additions & 0 deletions packages/media-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### New Features

- Rewrite in TypeScript, exporting all the individual utility functions.

## 5.7.0 (2024-09-05)

## 5.6.0 (2024-08-21)
Expand Down
71 changes: 70 additions & 1 deletion packages/media-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,75 @@ npm install @wordpress/media-utils --save

_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for such language features and APIs, you should include [the polyfill shipped in `@wordpress/babel-preset-default`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/babel-preset-default#polyfill) in your code._

## API

<!-- START TOKEN(Autogenerated API docs) -->

### Attachment

Undocumented declaration.

### MediaUpload

Undocumented declaration.

### RestAttachment

Undocumented declaration.

### transformAttachment

Transforms an attachment object from the REST API shape into the shape expected by the block editor and other consumers.

_Parameters_

- _attachment_ `RestAttachment`: REST API attachment object.

### uploadMedia

Upload a media file when the file upload button is activated or when adding a file to the editor via drag & drop.

_Parameters_

- _$0_ `UploadMediaArgs`: Parameters object passed to the function.
- _$0.allowedTypes_ `UploadMediaArgs[ 'allowedTypes' ]`: Array with the types of media that can be uploaded, if unset all types are allowed.
- _$0.additionalData_ `UploadMediaArgs[ 'additionalData' ]`: Additional data to include in the request.
- _$0.filesList_ `UploadMediaArgs[ 'filesList' ]`: List of files.
- _$0.maxUploadFileSize_ `UploadMediaArgs[ 'maxUploadFileSize' ]`: Maximum upload size in bytes allowed for the site.
- _$0.onError_ `UploadMediaArgs[ 'onError' ]`: Function called when an error happens.
- _$0.onFileChange_ `UploadMediaArgs[ 'onFileChange' ]`: Function called each time a file or a temporary representation of the file is available.
- _$0.wpAllowedMimeTypes_ `UploadMediaArgs[ 'wpAllowedMimeTypes' ]`: List of allowed mime types and file extensions.
- _$0.signal_ `UploadMediaArgs[ 'signal' ]`: Abort signal.

### validateFileSize

Verifies whether the file is within the file upload size limits for the site.

_Parameters_

- _file_ `File`: File object.
- _maxUploadFileSize_ `number`: Maximum upload size in bytes allowed for the site.

### validateMimeType

Verifies if the caller (e.g. a block) supports this mime type.

_Parameters_

- _file_ `File`: File object.
- _allowedTypes_ `string[]`: List of allowed mime types.

### validateMimeTypeForUser

Verifies if the user is allowed to upload this mime type.

_Parameters_

- _file_ `File`: File object.
- _wpAllowedMimeTypes_ `Record< string, string > | null`: List of allowed mime types and file extensions.

<!-- END TOKEN(Autogenerated API docs) -->

## Usage

### uploadMedia
Expand Down Expand Up @@ -43,7 +112,7 @@ Beware that first onFileChange is called with temporary blob URLs and then with
### MediaUpload

Media upload component provides a UI button that allows users to open the WordPress media library. It is normally used in conjunction with the filter `editor.MediaUpload`.
The component follows the interface specified in [https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/media-upload/README.md](https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/media-upload/README.md), and more details regarding its usage can be checked there.
The component follows the interface specified in <https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/media-upload/README.md>, and more details regarding its usage can be checked there.

## Contributing to this package

Expand Down
3 changes: 2 additions & 1 deletion packages/media-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"repository": {
"type": "git",
"url": "https://github.com/WordPress/gutenberg.git",
"directory": "packages/url"
"directory": "packages/media-utils"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
Expand All @@ -25,6 +25,7 @@
},
"main": "build/index.js",
"module": "build-module/index.js",
"types": "build-types",
"dependencies": {
"@babel/runtime": "^7.16.0",
"@wordpress/api-fetch": "file:../api-fetch",
Expand Down
2 changes: 0 additions & 2 deletions packages/media-utils/src/index.js

This file was deleted.

9 changes: 9 additions & 0 deletions packages/media-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export * from './components';

export { uploadMedia } from './utils/upload-media';
export { transformAttachment } from './utils/transform-attachment';
export { validateFileSize } from './utils/validate-file-size';
export { validateMimeType } from './utils/validate-mime-type';
export { validateMimeTypeForUser } from './utils/validate-mime-type-for-user';

export type { Attachment, RestAttachment } from './utils/types';
33 changes: 33 additions & 0 deletions packages/media-utils/src/utils/flatten-form-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Determines whether the passed argument appears to be a plain object.
*
* @param data The object to inspect.
*/
function isPlainObject( data: unknown ): data is Record< string, unknown > {
return (
data !== null &&
typeof data === 'object' &&
Object.getPrototypeOf( data ) === Object.prototype
);
}

/**
* Recursively flatten data passed to form data, to allow using multi-level objects.
*
* @param {FormData} formData Form data object.
* @param {string} key Key to amend to form data object
* @param {string|Object} data Data to be amended to form data.
*/
export function flattenFormData(
formData: FormData,
key: string,
data: string | undefined | Record< string, string >
) {
if ( isPlainObject( data ) ) {
for ( const [ name, value ] of Object.entries( data ) ) {
flattenFormData( formData, `${ key }[${ name }]`, value );
}
} else if ( data !== undefined ) {
formData.append( key, String( data ) );
}
}
29 changes: 29 additions & 0 deletions packages/media-utils/src/utils/get-mime-types-array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Browsers may use unexpected mime types, and they differ from browser to browser.
* This function computes a flexible array of mime types from the mime type structured provided by the server.
* Converts { jpg|jpeg|jpe: "image/jpeg" } into [ "image/jpeg", "image/jpg", "image/jpeg", "image/jpe" ]
*
* @param {?Object} wpMimeTypesObject Mime type object received from the server.
* Extensions are keys separated by '|' and values are mime types associated with an extension.
*
* @return An array of mime types or null
*/
export function getMimeTypesArray(
wpMimeTypesObject?: Record< string, string > | null
) {
if ( ! wpMimeTypesObject ) {
return null;
}
return Object.entries( wpMimeTypesObject ).flatMap(
( [ extensionsString, mime ] ) => {
const [ type ] = mime.split( '/' );
const extensions = extensionsString.split( '|' );
return [
mime,
...extensions.map(
( extension ) => `${ type }/${ extension }`
),
];
}
);
}
1 change: 0 additions & 1 deletion packages/media-utils/src/utils/index.js

This file was deleted.

49 changes: 49 additions & 0 deletions packages/media-utils/src/utils/test/flatten-form-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Internal dependencies
*/
import { flattenFormData } from '../flatten-form-data';

describe( 'flattenFormData', () => {
it( 'should flatten nested data structure', () => {
const data = new FormData();

class RichTextData {
toString() {
return 'i am rich text';
}
}

const additionalData = {
foo: null,
bar: 1234,
meta: {
nested: 'foo',
dothis: true,
dothat: false,
supermeta: {
nested: 'baz',
},
},
customClass: new RichTextData(),
};

for ( const [ key, value ] of Object.entries( additionalData ) ) {
flattenFormData(
data,
key,
value as Parameters< typeof flattenFormData >[ 2 ]
);
}

const actual = Object.fromEntries( data.entries() );
expect( actual ).toStrictEqual( {
bar: '1234',
foo: 'null',
'meta[dothat]': 'false',
'meta[dothis]': 'true',
'meta[nested]': 'foo',
'meta[supermeta][nested]': 'baz',
customClass: 'i am rich text',
} );
} );
} );
47 changes: 47 additions & 0 deletions packages/media-utils/src/utils/test/get-mime-types-array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Internal dependencies
*/
import { getMimeTypesArray } from '../get-mime-types-array';

describe( 'getMimeTypesArray', () => {
it( 'should return null if it is "falsy" e.g: undefined or null', () => {
expect( getMimeTypesArray( null ) ).toEqual( null );
expect( getMimeTypesArray( undefined ) ).toEqual( null );
} );

it( 'should return an empty array if an empty object is passed', () => {
expect( getMimeTypesArray( {} ) ).toEqual( [] );
} );

it( 'should return the type plus a new mime type with type and subtype with the extension if a type is passed', () => {
expect( getMimeTypesArray( { ext: 'chicken' } ) ).toEqual( [
'chicken',
'chicken/ext',
] );
} );

it( 'should return the mime type passed and a new mime type with type and the extension as subtype', () => {
expect( getMimeTypesArray( { ext: 'chicken/ribs' } ) ).toEqual( [
'chicken/ribs',
'chicken/ext',
] );
} );

it( 'should return the mime type passed and an additional mime type per extension supported', () => {
expect( getMimeTypesArray( { 'jpg|jpeg|jpe': 'image/jpeg' } ) ).toEqual(
[ 'image/jpeg', 'image/jpg', 'image/jpeg', 'image/jpe' ]
);
} );

it( 'should handle multiple mime types', () => {
expect(
getMimeTypesArray( { 'ext|aaa': 'chicken/ribs', aaa: 'bbb' } )
).toEqual( [
'chicken/ribs',
'chicken/ext',
'chicken/aaa',
'bbb',
'bbb/aaa',
] );
} );
} );
24 changes: 24 additions & 0 deletions packages/media-utils/src/utils/test/upload-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Internal dependencies
*/
import { UploadError } from '../upload-error';

describe( 'UploadError', () => {
it( 'holds error code and file name', () => {
const file = new File( [], 'example.jpg', {
lastModified: 1234567891,
type: 'image/jpeg',
} );

const error = new UploadError( {
code: 'some_error',
message: 'An error occurred',
file,
} );

expect( error ).toStrictEqual( expect.any( Error ) );
expect( error.code ).toBe( 'some_error' );
expect( error.message ).toBe( 'An error occurred' );
expect( error.file ).toBe( file );
} );
} );
Loading

0 comments on commit f4cad96

Please sign in to comment.