From de5471ffd54640012c77fabf418eabee96de68ec Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Mon, 22 May 2023 12:42:33 -0700 Subject: [PATCH 1/2] Add helper to get file type icon URL --- .../src/getFileTypeIconAsHTMLString.ts | 14 +-- .../src/getFileTypeIconAsURL.test.ts | 98 +++++++++++++++++++ .../src/getFileTypeIconAsURL.ts | 32 ++++++ packages/react-file-type-icons/src/index.ts | 2 + .../src/initializeFileTypeIcons.tsx | 3 +- 5 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 packages/react-file-type-icons/src/getFileTypeIconAsURL.test.ts create mode 100644 packages/react-file-type-icons/src/getFileTypeIconAsURL.ts diff --git a/packages/react-file-type-icons/src/getFileTypeIconAsHTMLString.ts b/packages/react-file-type-icons/src/getFileTypeIconAsHTMLString.ts index ce9ddd23ac081c..9d01bb2ded522c 100644 --- a/packages/react-file-type-icons/src/getFileTypeIconAsHTMLString.ts +++ b/packages/react-file-type-icons/src/getFileTypeIconAsHTMLString.ts @@ -1,10 +1,7 @@ import { DEFAULT_BASE_URL } from './initializeFileTypeIcons'; -import { - getFileTypeIconNameFromExtensionOrType, - getFileTypeIconSuffix, - DEFAULT_ICON_SIZE, -} from './getFileTypeIconProps'; +import { getFileTypeIconSuffix, DEFAULT_ICON_SIZE } from './getFileTypeIconProps'; import type { IFileTypeIconOptions } from './getFileTypeIconProps'; +import { getFileTypeIconAsURL } from './getFileTypeIconAsURL'; /** * Given the `fileTypeIconOptions`, this function returns the DOM element for the `FileTypeIcon` @@ -17,19 +14,16 @@ export function getFileTypeIconAsHTMLString( options: IFileTypeIconOptions, baseUrl: string = DEFAULT_BASE_URL, ): string | undefined { - const { extension, size = DEFAULT_ICON_SIZE, type, imageFileType } = options; - const baseIconName = getFileTypeIconNameFromExtensionOrType(extension, type); // eg: docx + const { size = DEFAULT_ICON_SIZE, imageFileType } = options; const baseSuffix = getFileTypeIconSuffix(size, imageFileType); // eg: 96_3x_svg or 96_png const suffixArray = baseSuffix.split('_'); // eg: ['96', '3x', 'svg'] - let src: string | undefined; + const src = getFileTypeIconAsURL(options, baseUrl); if (suffixArray.length === 3) { /** suffix is of type 96_3x_svg - it has a pixel ratio > 1*/ - src = `${baseUrl}${size}_${suffixArray[1]}/${baseIconName}.${suffixArray[2]}`; return ``; } else if (suffixArray.length === 2) { /** suffix is of type 96_svg - it has a pixel ratio of 1*/ - src = `${baseUrl}${size}/${baseIconName}.${suffixArray[1]}`; return ``; } } diff --git a/packages/react-file-type-icons/src/getFileTypeIconAsURL.test.ts b/packages/react-file-type-icons/src/getFileTypeIconAsURL.test.ts new file mode 100644 index 00000000000000..e2e77f8b11a8ed --- /dev/null +++ b/packages/react-file-type-icons/src/getFileTypeIconAsURL.test.ts @@ -0,0 +1,98 @@ +import { ICON_SIZES, DEFAULT_BASE_URL } from './initializeFileTypeIcons'; +import { DEFAULT_ICON_SIZE } from './getFileTypeIconProps'; +import { getFileTypeIconAsURL } from './getFileTypeIconAsURL'; +import type { FileTypeIconSize } from './getFileTypeIconProps'; + +// Currently this test file only covers the default device pixel ratio, i.e 1 +const getExpectedURL = (iconSize: FileTypeIconSize, suffix: string, expectedExt: string) => { + return `${DEFAULT_BASE_URL}${iconSize}/${expectedExt}.${suffix}`; +}; + +// Test suite 1 +describe('returns valid URLs', () => { + it('returns the correct url for all valid icon sizes with default as svg', () => { + ICON_SIZES.forEach((iconSize: number) => { + const res = getFileTypeIconAsURL({ + size: iconSize as FileTypeIconSize, + extension: 'doc', + }); + expect(res).toEqual(getExpectedURL(iconSize as FileTypeIconSize, 'svg', 'docx')); + }); + + ICON_SIZES.forEach((iconSize: number) => { + const res = getFileTypeIconAsURL({ + size: iconSize as FileTypeIconSize, + extension: 'accdb', + }); + expect(res).toEqual(getExpectedURL(iconSize as FileTypeIconSize, 'svg', 'accdb')); + }); + }); + + it('returns the correct url for all valid icon sizes with type as png', () => { + ICON_SIZES.forEach((iconSize: number) => { + const res = getFileTypeIconAsURL({ + size: iconSize as FileTypeIconSize, + extension: 'doc', + imageFileType: 'png', + }); + expect(res).toEqual(getExpectedURL(iconSize as FileTypeIconSize, 'png', 'docx')); + }); + + ICON_SIZES.forEach((iconSize: number) => { + const res = getFileTypeIconAsURL({ + size: iconSize as FileTypeIconSize, + extension: 'accdb', + imageFileType: 'png', + }); + expect(res).toEqual(getExpectedURL(iconSize as FileTypeIconSize, 'png', 'accdb')); + }); + }); +}); + +// Test suite 2 +describe('Returns genericfile for invalid inputs', () => { + it('returns genericfile for invalid extension with default type as svg', () => { + ICON_SIZES.forEach((iconSize: number) => { + const res = getFileTypeIconAsURL({ + size: iconSize as FileTypeIconSize, + extension: 'blah', + }); + expect(res).toEqual(getExpectedURL(iconSize as FileTypeIconSize, 'svg', 'genericfile')); + }); + }); + + it('returns genericfile with type as png', () => { + ICON_SIZES.forEach((iconSize: number) => { + const res = getFileTypeIconAsURL({ + size: iconSize as FileTypeIconSize, + extension: 'NotAValidExtension', + imageFileType: 'png', + }); + expect(res).toEqual(getExpectedURL(iconSize as FileTypeIconSize, 'png', 'genericfile')); + }); + }); + + it('returns genericfile with default size for empty size, extension and type', () => { + const res = getFileTypeIconAsURL({}); + expect(res).toEqual(getExpectedURL(DEFAULT_ICON_SIZE, 'svg', 'genericfile')); + }); + + it('returns genericfile with default size for empty size, extension and type with type as png', () => { + const res = getFileTypeIconAsURL({ imageFileType: 'png' }); + expect(res).toEqual(getExpectedURL(DEFAULT_ICON_SIZE, 'png', 'genericfile')); + }); +}); + +// Test suite 3 +describe('Returns correct element for custom CDN url', () => { + it('returns expected url', () => { + const elm = getFileTypeIconAsURL( + { + size: 96, + extension: 'docx', + }, + 'https://example-base-url/assets/item-types-fluent/', + ); + expect(elm).toEqual('https://example-base-url/assets/item-types-fluent/96/docx.svg'); + }); +}); diff --git a/packages/react-file-type-icons/src/getFileTypeIconAsURL.ts b/packages/react-file-type-icons/src/getFileTypeIconAsURL.ts new file mode 100644 index 00000000000000..2d85de15f6378a --- /dev/null +++ b/packages/react-file-type-icons/src/getFileTypeIconAsURL.ts @@ -0,0 +1,32 @@ +import { DEFAULT_BASE_URL } from './initializeFileTypeIcons'; +import { + getFileTypeIconNameFromExtensionOrType, + getFileTypeIconSuffix, + DEFAULT_ICON_SIZE, +} from './getFileTypeIconProps'; +import type { IFileTypeIconOptions } from './getFileTypeIconProps'; + +/** + * Given the `fileTypeIconOptions`, this function returns the image for the `FileTypeIcon` + * as an URL. Similar to `getFileTypeIconProps`, this also accepts the same type of object + * but rather than returning the `iconName`, this returns the entire image URL as a string. + * @param options + * @param baseUrl - optionally provide a custom CDN base url to fetch icons from + */ +export function getFileTypeIconAsURL( + options: IFileTypeIconOptions, + baseUrl: string = DEFAULT_BASE_URL, +): string | undefined { + const { extension, size = DEFAULT_ICON_SIZE, type, imageFileType } = options; + const baseIconName = getFileTypeIconNameFromExtensionOrType(extension, type); // eg: docx + const baseSuffix = getFileTypeIconSuffix(size, imageFileType); // eg: 96_3x_svg or 96_png + const suffixArray = baseSuffix.split('_'); // eg: ['96', '3x', 'svg'] + + if (suffixArray.length === 3) { + /** suffix is of type 96_3x_svg - it has a pixel ratio > 1*/ + return `${baseUrl}${size}_${suffixArray[1]}/${baseIconName}.${suffixArray[2]}`; + } else if (suffixArray.length === 2) { + /** suffix is of type 96_svg - it has a pixel ratio of 1*/ + return `${baseUrl}${size}/${baseIconName}.${suffixArray[1]}`; + } +} diff --git a/packages/react-file-type-icons/src/index.ts b/packages/react-file-type-icons/src/index.ts index 4a3691c312cfbc..7cb115980aa615 100644 --- a/packages/react-file-type-icons/src/index.ts +++ b/packages/react-file-type-icons/src/index.ts @@ -8,6 +8,8 @@ export { FileTypeIconMap } from './FileTypeIconMap'; export { getFileTypeIconAsHTMLString } from './getFileTypeIconAsHTMLString'; +export { getFileTypeIconAsURL } from './getFileTypeIconAsURL'; + import './version'; export type { FileTypeIconSize, IFileTypeIconOptions, ImageFileType } from './getFileTypeIconProps'; diff --git a/packages/react-file-type-icons/src/initializeFileTypeIcons.tsx b/packages/react-file-type-icons/src/initializeFileTypeIcons.tsx index d13b34e54f9795..8ea0af8aedc1e4 100644 --- a/packages/react-file-type-icons/src/initializeFileTypeIcons.tsx +++ b/packages/react-file-type-icons/src/initializeFileTypeIcons.tsx @@ -2,12 +2,13 @@ import * as React from 'react'; import { registerIcons, FLUENT_CDN_BASE_URL } from '@fluentui/style-utilities'; import { FileTypeIconMap } from './FileTypeIconMap'; import type { IIconOptions } from '@fluentui/style-utilities'; +import type { FileTypeIconSize } from './getFileTypeIconProps'; const PNG_SUFFIX = '_png'; const SVG_SUFFIX = '_svg'; export const DEFAULT_BASE_URL = `${FLUENT_CDN_BASE_URL}/assets/item-types/`; -export const ICON_SIZES: number[] = [16, 20, 24, 32, 40, 48, 64, 96]; +export const ICON_SIZES: FileTypeIconSize[] = [16, 20, 24, 32, 40, 48, 64, 96]; export function initializeFileTypeIcons(baseUrl: string = DEFAULT_BASE_URL, options?: Partial): void { ICON_SIZES.forEach((size: number) => { From 026619a02518535e825617f47d55533a14f0129f Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Mon, 22 May 2023 12:43:44 -0700 Subject: [PATCH 2/2] Changelog --- ...le-type-icons-e373acc1-0c3c-491b-baf5-208cf47dfffa.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-file-type-icons-e373acc1-0c3c-491b-baf5-208cf47dfffa.json diff --git a/change/@fluentui-react-file-type-icons-e373acc1-0c3c-491b-baf5-208cf47dfffa.json b/change/@fluentui-react-file-type-icons-e373acc1-0c3c-491b-baf5-208cf47dfffa.json new file mode 100644 index 00000000000000..ff3bec59b9ab7e --- /dev/null +++ b/change/@fluentui-react-file-type-icons-e373acc1-0c3c-491b-baf5-208cf47dfffa.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Expose getFileTypeIconAsURL helper to get file type icon url instead of HTML element", + "packageName": "@fluentui/react-file-type-icons", + "email": "tigeroakes@microsoft.com", + "dependentChangeType": "patch" +}