From 9fba299b30d0e5c2be774974543a15cb2a6167b7 Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Mon, 23 Sep 2024 13:24:02 -0700 Subject: [PATCH 01/69] Adding `portfolio` (Microsoft Project item type) to the list of non-fileextension based filetype icons. --- packages/react-file-type-icons/src/FileIconType.ts | 3 ++- packages/react-file-type-icons/src/FileTypeIconMap.ts | 1 + packages/react-file-type-icons/src/getFileTypeIconProps.ts | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/react-file-type-icons/src/FileIconType.ts b/packages/react-file-type-icons/src/FileIconType.ts index 8e8b92b2651239..afed4fef159a17 100644 --- a/packages/react-file-type-icons/src/FileIconType.ts +++ b/packages/react-file-type-icons/src/FileIconType.ts @@ -25,6 +25,7 @@ export enum FileIconType { loopworkspace = 17, planner = 18, todoItem = 19, + portfolio = 20, } -export type FileIconTypeInput = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19; +export type FileIconTypeInput = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20; diff --git a/packages/react-file-type-icons/src/FileTypeIconMap.ts b/packages/react-file-type-icons/src/FileTypeIconMap.ts index 131ccaf90cbcfc..1a6956ec6f5b87 100644 --- a/packages/react-file-type-icons/src/FileTypeIconMap.ts +++ b/packages/react-file-type-icons/src/FileTypeIconMap.ts @@ -407,6 +407,7 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { photo360: {}, picturesfolder: {}, planner: {}, + portfolio: {}, potx: { extensions: ['pot', 'potm', 'potx'], }, diff --git a/packages/react-file-type-icons/src/getFileTypeIconProps.ts b/packages/react-file-type-icons/src/getFileTypeIconProps.ts index 2bf8d49d07809e..30fbb13a0eded0 100644 --- a/packages/react-file-type-icons/src/getFileTypeIconProps.ts +++ b/packages/react-file-type-icons/src/getFileTypeIconProps.ts @@ -23,6 +23,7 @@ const PLAYLIST = 'playlist'; const LOOP_WORKSPACE = 'loopworkspace'; const TODOITEM = 'todoitem'; const PLANNER = 'planner'; +const PORTFOLIO = 'portfolio' export const DEFAULT_ICON_SIZE: FileTypeIconSize = 16; export type FileTypeIconSize = 16 | 20 | 24 | 32 | 40 | 48 | 64 | 96; @@ -155,6 +156,9 @@ export function getFileTypeIconNameFromExtensionOrType( case FileIconType.todoItem: iconBaseName = TODOITEM; break; + case FileIconType.portfolio: + iconBaseName = PORTFOLIO; + break; } } return iconBaseName || GENERIC_FILE; From 63f928c8f0ad9e9912c4f1f3eae8851a62c6c11c Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Mon, 23 Sep 2024 14:43:43 -0700 Subject: [PATCH 02/69] More filetype support. --- packages/react-file-type-icons/src/FileTypeIconMap.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-file-type-icons/src/FileTypeIconMap.ts b/packages/react-file-type-icons/src/FileTypeIconMap.ts index 1a6956ec6f5b87..e8fb28a7e224eb 100644 --- a/packages/react-file-type-icons/src/FileTypeIconMap.ts +++ b/packages/react-file-type-icons/src/FileTypeIconMap.ts @@ -258,6 +258,9 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { contact: { extensions: ['vcf'], }, + copilot: { + extensions: ['copilot'], + } /* css: {}, not broken out yet, snapping to 'code' for now */ csv: { extensions: ['csv'], From 3ccf0e36267326844db861ef1b2417c8c2ee15b7 Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Wed, 25 Sep 2024 13:50:40 -0700 Subject: [PATCH 03/69] update CDN url and typo --- packages/react-file-type-icons/src/FileTypeIconMap.ts | 2 +- packages/style-utilities/src/cdn.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-file-type-icons/src/FileTypeIconMap.ts b/packages/react-file-type-icons/src/FileTypeIconMap.ts index e8fb28a7e224eb..26e75ff92d0595 100644 --- a/packages/react-file-type-icons/src/FileTypeIconMap.ts +++ b/packages/react-file-type-icons/src/FileTypeIconMap.ts @@ -260,7 +260,7 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { }, copilot: { extensions: ['copilot'], - } + }, /* css: {}, not broken out yet, snapping to 'code' for now */ csv: { extensions: ['csv'], diff --git a/packages/style-utilities/src/cdn.ts b/packages/style-utilities/src/cdn.ts index bd32b80c3814d5..673c6e235641f9 100644 --- a/packages/style-utilities/src/cdn.ts +++ b/packages/style-utilities/src/cdn.ts @@ -1 +1 @@ -export const FLUENT_CDN_BASE_URL = 'https://res.cdn.office.net/files/fabric-cdn-prod_20240805.001'; +export const FLUENT_CDN_BASE_URL = 'https://res.cdn.office.net/files/fabric-cdn-prod_20240925.001'; From fab6a3af3069f06b4e3218555e85ce867b1b5ec1 Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Wed, 25 Sep 2024 14:43:58 -0700 Subject: [PATCH 04/69] yarn change --- ...le-type-icons-4b02a3cb-433c-4e04-a104-ccd0c48b49b4.json | 7 +++++++ ...yle-utilities-c0995633-6083-4fb4-a35e-135530e2158a.json | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 change/@fluentui-react-file-type-icons-4b02a3cb-433c-4e04-a104-ccd0c48b49b4.json create mode 100644 change/@fluentui-style-utilities-c0995633-6083-4fb4-a35e-135530e2158a.json diff --git a/change/@fluentui-react-file-type-icons-4b02a3cb-433c-4e04-a104-ccd0c48b49b4.json b/change/@fluentui-react-file-type-icons-4b02a3cb-433c-4e04-a104-ccd0c48b49b4.json new file mode 100644 index 00000000000000..8855bce398d8d0 --- /dev/null +++ b/change/@fluentui-react-file-type-icons-4b02a3cb-433c-4e04-a104-ccd0c48b49b4.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "\u0016Update to filetype icons - Sep 2024", + "packageName": "@fluentui/react-file-type-icons", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-style-utilities-c0995633-6083-4fb4-a35e-135530e2158a.json b/change/@fluentui-style-utilities-c0995633-6083-4fb4-a35e-135530e2158a.json new file mode 100644 index 00000000000000..a4a450f6729f69 --- /dev/null +++ b/change/@fluentui-style-utilities-c0995633-6083-4fb4-a35e-135530e2158a.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Update to filetype icons - Sep 2024", + "packageName": "@fluentui/style-utilities", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} From 55294b5bdf5c5f067342d5579b875e9998fe4b06 Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Wed, 25 Sep 2024 22:10:48 -0700 Subject: [PATCH 05/69] API update (from yarn nx run-many -t build) --- packages/style-utilities/etc/style-utilities.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/style-utilities/etc/style-utilities.api.md b/packages/style-utilities/etc/style-utilities.api.md index 44b865d252b05e..ac18c4a79b52b7 100644 --- a/packages/style-utilities/etc/style-utilities.api.md +++ b/packages/style-utilities/etc/style-utilities.api.md @@ -84,7 +84,7 @@ export { DefaultPalette } export const EdgeChromiumHighContrastSelector = "@media screen and (-ms-high-contrast: active), screen and (forced-colors: active)"; // @public (undocumented) -export const FLUENT_CDN_BASE_URL = "https://res.cdn.office.net/files/fabric-cdn-prod_20240805.001"; +export const FLUENT_CDN_BASE_URL = "https://res.cdn.office.net/files/fabric-cdn-prod_20240925.001"; // @public export function focusClear(): IRawStyle; From bbeb40a341ebe047ef1cb2f79f83d62bea99c24c Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Thu, 26 Sep 2024 08:40:00 -0700 Subject: [PATCH 06/69] Ran `yarn nx format` --- .../react-file-type-icons/src/FileIconType.ts | 22 ++++++++++++++++++- .../src/getFileTypeIconProps.ts | 6 ++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/react-file-type-icons/src/FileIconType.ts b/packages/react-file-type-icons/src/FileIconType.ts index afed4fef159a17..edc2668d81b45d 100644 --- a/packages/react-file-type-icons/src/FileIconType.ts +++ b/packages/react-file-type-icons/src/FileIconType.ts @@ -28,4 +28,24 @@ export enum FileIconType { portfolio = 20, } -export type FileIconTypeInput = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20; +export type FileIconTypeInput = + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17 + | 18 + | 19 + | 20; diff --git a/packages/react-file-type-icons/src/getFileTypeIconProps.ts b/packages/react-file-type-icons/src/getFileTypeIconProps.ts index 30fbb13a0eded0..a7da544dca1d42 100644 --- a/packages/react-file-type-icons/src/getFileTypeIconProps.ts +++ b/packages/react-file-type-icons/src/getFileTypeIconProps.ts @@ -23,7 +23,7 @@ const PLAYLIST = 'playlist'; const LOOP_WORKSPACE = 'loopworkspace'; const TODOITEM = 'todoitem'; const PLANNER = 'planner'; -const PORTFOLIO = 'portfolio' +const PORTFOLIO = 'portfolio'; export const DEFAULT_ICON_SIZE: FileTypeIconSize = 16; export type FileTypeIconSize = 16 | 20 | 24 | 32 | 40 | 48 | 64 | 96; @@ -157,8 +157,8 @@ export function getFileTypeIconNameFromExtensionOrType( iconBaseName = TODOITEM; break; case FileIconType.portfolio: - iconBaseName = PORTFOLIO; - break; + iconBaseName = PORTFOLIO; + break; } } return iconBaseName || GENERIC_FILE; From 280fe2c926cc53ad7f9e1f52c4ab99e476671bca Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Sat, 28 Sep 2024 22:35:12 -0700 Subject: [PATCH 07/69] Changing FileIconType-based icon searching to use the same scalable method used for file extensions --- .../react-file-type-icons/src/FileIconType.ts | 59 +++------- .../src/FileTypeIconMap.ts | 82 +++++++++---- .../src/getFileTypeIconProps.ts | 110 ++++-------------- 3 files changed, 104 insertions(+), 147 deletions(-) diff --git a/packages/react-file-type-icons/src/FileIconType.ts b/packages/react-file-type-icons/src/FileIconType.ts index edc2668d81b45d..5dc6d530d117aa 100644 --- a/packages/react-file-type-icons/src/FileIconType.ts +++ b/packages/react-file-type-icons/src/FileIconType.ts @@ -7,45 +7,24 @@ export enum FileIconType { docset = 1, // Start at 1 so it will evaluate as "truthy" - folder = 2, - genericFile = 3, - listItem = 4, - sharedFolder = 5, - multiple = 6, - stream = 7, - news = 8, - desktopFolder = 9, - documentsFolder = 10, - picturesFolder = 11, - linkedFolder = 12, - list = 13, - form = 14, - sway = 15, - playlist = 16, - loopworkspace = 17, - planner = 18, - todoItem = 19, - portfolio = 20, + folder, + genericFile, + listItem, + sharedFolder, + multiple, + stream, + news, + desktopFolder, + documentsFolder, + picturesFolder, + linkedFolder, + list, + form, + sway, + playlist, + loopworkspace, + planner, + todoItem, + portfolio, } -export type FileIconTypeInput = - | 1 - | 2 - | 3 - | 4 - | 5 - | 6 - | 7 - | 8 - | 9 - | 10 - | 11 - | 12 - | 13 - | 14 - | 15 - | 16 - | 17 - | 18 - | 19 - | 20; diff --git a/packages/react-file-type-icons/src/FileTypeIconMap.ts b/packages/react-file-type-icons/src/FileTypeIconMap.ts index 26e75ff92d0595..bc6987c06b3940 100644 --- a/packages/react-file-type-icons/src/FileTypeIconMap.ts +++ b/packages/react-file-type-icons/src/FileTypeIconMap.ts @@ -1,9 +1,11 @@ +import { FileIconType } from './FileIconType'; + /** * Enumeration of icon file names, and what extensions they map to. * Please keep items alphabetical. Items without extensions may require specific logic in the code to map. * Always use getFileTypeIconProps to get the most up-to-date icon at the right pixel density. */ -export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { +export const FileTypeIconMap: { [key: string]: { extensions?: string[], types?: FileIconType[] } } = { accdb: { extensions: ['accdb', 'mdb'], }, @@ -268,9 +270,15 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { designer: { extensions: ['design'], }, - desktopfolder: {}, - docset: {}, - documentsfolder: {}, + desktopfolder: { + types: [FileIconType.desktopFolder] + }, + docset: { + types: [FileIconType.docset] + }, + documentsfolder: { + types: [FileIconType.documentsFolder] + }, docx: { extensions: ['doc', 'docm', 'docx', 'docb'], }, @@ -283,13 +291,18 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { exe: { extensions: ['application', 'appref-ms', 'apk', 'app', 'appx', 'exe', 'ipa', 'msi', 'xap'], }, - favoritesfolder: {}, - folder: {}, + folder: { + types: [FileIconType.folder] + }, font: { extensions: ['ttf', 'otf', 'woff'], }, - form: {}, - genericfile: {}, + form: { + types: [FileIconType.form] + }, + genericfile: { + types: [FileIconType.genericFile] + }, html: { extensions: ['htm', 'html', 'mht', 'mhtml'], }, @@ -299,16 +312,24 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { link: { extensions: ['lnk', 'link', 'url', 'website', 'webloc'], }, - linkedfolder: {}, - listitem: {}, + linkedfolder: { + types: [FileIconType.linkedFolder] + }, + listitem: { + types: [FileIconType.listItem] + }, loop: { extensions: ['fluid', 'loop', 'note'], }, - loopworkspace: {}, + loopworkspace: { + types: [FileIconType.loopworkspace] + }, officescript: { extensions: ['osts'], }, - splist: {}, + splist: { + types: [FileIconType.list] + }, mcworld: { extensions: ['mcworld'], }, @@ -351,7 +372,9 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { mpt: { extensions: ['mpt'], }, - multiple: {}, + multiple: { + types: [FileIconType.multiple] + }, one: { // This is a partial OneNote page or section export. Not whole notebooks, see "onetoc" extensions: ['one'], @@ -408,9 +431,15 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { ], }, photo360: {}, - picturesfolder: {}, - planner: {}, - portfolio: {}, + picturesfolder: { + types: [FileIconType.picturesFolder] + }, + planner: { + types: [FileIconType.planner] + }, + portfolio: { + types: [FileIconType.portfolio] + }, potx: { extensions: ['pot', 'potm', 'potx'], }, @@ -432,16 +461,24 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { spo: { extensions: ['aspx'], }, - sponews: {}, + sponews: { + types: [FileIconType.news] + }, spreadsheet: { extensions: ['odc', 'ods', 'gsheet', 'numbers', 'tsv'], }, rtf: { extensions: ['epub', 'gdoc', 'odt', 'rtf', 'wri', 'pages'], }, - sharedfolder: {}, - playlist: {}, - sway: {}, + sharedfolder: { + types: [FileIconType.sharedFolder] + }, + playlist: { + types: [FileIconType.playlist] + }, + sway: { + types: [FileIconType.sway] + }, sysfile: { extensions: [ 'bak', @@ -475,7 +512,9 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { 'xll', ], }, - todoitem: {}, + todoitem: { + types: [FileIconType.todoItem] + }, txt: { extensions: ['dif', 'diff', 'readme', 'out', 'plist', 'properties', 'text', 'txt'], }, @@ -508,6 +547,7 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { ], }, video: { + types: [FileIconType.stream], extensions: [ '3g2', '3gp', diff --git a/packages/react-file-type-icons/src/getFileTypeIconProps.ts b/packages/react-file-type-icons/src/getFileTypeIconProps.ts index a7da544dca1d42..e4011e1fd33bb5 100644 --- a/packages/react-file-type-icons/src/getFileTypeIconProps.ts +++ b/packages/react-file-type-icons/src/getFileTypeIconProps.ts @@ -1,29 +1,10 @@ import { FileTypeIconMap } from './FileTypeIconMap'; import { FileIconType } from './FileIconType'; -import type { FileIconTypeInput } from './FileIconType'; let _extensionToIconName: { [key: string]: string }; +let _typeToIconName: { [key:number]: string}; const GENERIC_FILE = 'genericfile'; -const FOLDER = 'folder'; -const SHARED_FOLDER = 'sharedfolder'; -const DOCSET_FOLDER = 'docset'; -const LIST_ITEM = 'listitem'; -const LIST = 'splist'; -const MULTIPLE_ITEMS = 'multiple'; -const NEWS = 'sponews'; -const STREAM = 'video'; -const DESKTOP_FOLDER = 'desktopfolder'; -const DOCUMENTS_FOLDER = 'documentsfolder'; -const PICTURES_FOLDER = 'picturesfolder'; -const LINKED_FOLDER = 'linkedfolder'; -const FORM = 'form'; -const SWAY = 'sway'; -const PLAYLIST = 'playlist'; -const LOOP_WORKSPACE = 'loopworkspace'; -const TODOITEM = 'todoitem'; -const PLANNER = 'planner'; -const PORTFOLIO = 'portfolio'; export const DEFAULT_ICON_SIZE: FileTypeIconSize = 16; export type FileTypeIconSize = 16 | 20 | 24 | 32 | 40 | 48 | 64 | 96; @@ -41,7 +22,7 @@ export interface IFileTypeIconOptions { * file type icons that are not associated with a file extension, * such as folder. */ - type?: FileIconTypeInput; + type?: FileIconType; /** * The size of the icon in pixels. * @default 16 @@ -85,13 +66,11 @@ export function getFileTypeIconNameFromExtensionOrType( _extensionToIconName = {}; for (const iconName in FileTypeIconMap) { - if (FileTypeIconMap.hasOwnProperty(iconName)) { - const extensions = FileTypeIconMap[iconName].extensions; + const extensions = FileTypeIconMap[iconName].extensions; - if (extensions) { - for (let i = 0; i < extensions.length; i++) { - _extensionToIconName[extensions[i]] = iconName; - } + if (extensions) { + for (let i = 0; i < extensions.length; i++) { + _extensionToIconName[extensions[i]] = iconName; } } } @@ -100,68 +79,27 @@ export function getFileTypeIconNameFromExtensionOrType( // Strip periods, force lowercase. extension = extension.replace('.', '').toLowerCase(); return _extensionToIconName[extension] || GENERIC_FILE; + } else if (type) { - switch (type) { - case FileIconType.docset: - iconBaseName = DOCSET_FOLDER; - break; - case FileIconType.folder: - iconBaseName = FOLDER; - break; - case FileIconType.listItem: - iconBaseName = LIST_ITEM; - break; - case FileIconType.sharedFolder: - iconBaseName = SHARED_FOLDER; - break; - case FileIconType.stream: - iconBaseName = STREAM; - break; - case FileIconType.multiple: - iconBaseName = MULTIPLE_ITEMS; - break; - case FileIconType.news: - iconBaseName = NEWS; - break; - case FileIconType.desktopFolder: - iconBaseName = DESKTOP_FOLDER; - break; - case FileIconType.documentsFolder: - iconBaseName = DOCUMENTS_FOLDER; - break; - case FileIconType.picturesFolder: - iconBaseName = PICTURES_FOLDER; - break; - case FileIconType.linkedFolder: - iconBaseName = LINKED_FOLDER; - break; - case FileIconType.list: - iconBaseName = LIST; - break; - case FileIconType.form: - iconBaseName = FORM; - break; - case FileIconType.sway: - iconBaseName = SWAY; - break; - case FileIconType.playlist: - iconBaseName = PLAYLIST; - break; - case FileIconType.loopworkspace: - iconBaseName = LOOP_WORKSPACE; - break; - case FileIconType.planner: - iconBaseName = PLANNER; - break; - case FileIconType.todoItem: - iconBaseName = TODOITEM; - break; - case FileIconType.portfolio: - iconBaseName = PORTFOLIO; - break; + + if (!_typeToIconName) { + _typeToIconName = {}; + + for (const iconName in FileTypeIconMap) { + const types = FileTypeIconMap[iconName].types; + + if (types) { + for (let i = 0; i < types.length; i++) { + _typeToIconName[types[i]] = iconName + } + } + } } + + return _typeToIconName[type] || GENERIC_FILE; } - return iconBaseName || GENERIC_FILE; + + return GENERIC_FILE; } export function getFileTypeIconSuffix( From d9cc6569b0931fba8cf12d71007bf8c32a1e7b51 Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Mon, 30 Sep 2024 10:24:55 -0700 Subject: [PATCH 08/69] removing stray outdated yarn change files --- ...le-type-icons-4b02a3cb-433c-4e04-a104-ccd0c48b49b4.json | 7 ------- ...yle-utilities-c0995633-6083-4fb4-a35e-135530e2158a.json | 7 ------- 2 files changed, 14 deletions(-) delete mode 100644 change/@fluentui-react-file-type-icons-4b02a3cb-433c-4e04-a104-ccd0c48b49b4.json delete mode 100644 change/@fluentui-style-utilities-c0995633-6083-4fb4-a35e-135530e2158a.json diff --git a/change/@fluentui-react-file-type-icons-4b02a3cb-433c-4e04-a104-ccd0c48b49b4.json b/change/@fluentui-react-file-type-icons-4b02a3cb-433c-4e04-a104-ccd0c48b49b4.json deleted file mode 100644 index 8855bce398d8d0..00000000000000 --- a/change/@fluentui-react-file-type-icons-4b02a3cb-433c-4e04-a104-ccd0c48b49b4.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "minor", - "comment": "\u0016Update to filetype icons - Sep 2024", - "packageName": "@fluentui/react-file-type-icons", - "email": "caperez@microsoft.com", - "dependentChangeType": "patch" -} diff --git a/change/@fluentui-style-utilities-c0995633-6083-4fb4-a35e-135530e2158a.json b/change/@fluentui-style-utilities-c0995633-6083-4fb4-a35e-135530e2158a.json deleted file mode 100644 index a4a450f6729f69..00000000000000 --- a/change/@fluentui-style-utilities-c0995633-6083-4fb4-a35e-135530e2158a.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "minor", - "comment": "Update to filetype icons - Sep 2024", - "packageName": "@fluentui/style-utilities", - "email": "caperez@microsoft.com", - "dependentChangeType": "patch" -} From a7884fbb9b1729f58ccb9757ffe2fe0c1dde9578 Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Mon, 30 Sep 2024 10:27:04 -0700 Subject: [PATCH 09/69] yarn change --- ...le-type-icons-0a69cb3d-cf81-4967-8961-6a2ab277c675.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-file-type-icons-0a69cb3d-cf81-4967-8961-6a2ab277c675.json diff --git a/change/@fluentui-react-file-type-icons-0a69cb3d-cf81-4967-8961-6a2ab277c675.json b/change/@fluentui-react-file-type-icons-0a69cb3d-cf81-4967-8961-6a2ab277c675.json new file mode 100644 index 00000000000000..09a06ece5d58af --- /dev/null +++ b/change/@fluentui-react-file-type-icons-0a69cb3d-cf81-4967-8961-6a2ab277c675.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "\u0016\u0016\u0016Chagnging FileIconType-based icon searchign to use the same scalable method used for file extensions", + "packageName": "@fluentui/react-file-type-icons", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} From b12c050be04f1a1dcad1bb1a4883e6f75daa67b2 Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Mon, 30 Sep 2024 11:16:12 -0700 Subject: [PATCH 10/69] Ran `yarn nx format` --- .../react-file-type-icons/src/FileIconType.ts | 1 - .../src/FileTypeIconMap.ts | 48 +++++++++---------- .../src/getFileTypeIconProps.ts | 6 +-- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/packages/react-file-type-icons/src/FileIconType.ts b/packages/react-file-type-icons/src/FileIconType.ts index 5dc6d530d117aa..b567c6d8c3c6db 100644 --- a/packages/react-file-type-icons/src/FileIconType.ts +++ b/packages/react-file-type-icons/src/FileIconType.ts @@ -27,4 +27,3 @@ export enum FileIconType { todoItem, portfolio, } - diff --git a/packages/react-file-type-icons/src/FileTypeIconMap.ts b/packages/react-file-type-icons/src/FileTypeIconMap.ts index bc6987c06b3940..ccaf39a8582ad4 100644 --- a/packages/react-file-type-icons/src/FileTypeIconMap.ts +++ b/packages/react-file-type-icons/src/FileTypeIconMap.ts @@ -5,7 +5,7 @@ import { FileIconType } from './FileIconType'; * Please keep items alphabetical. Items without extensions may require specific logic in the code to map. * Always use getFileTypeIconProps to get the most up-to-date icon at the right pixel density. */ -export const FileTypeIconMap: { [key: string]: { extensions?: string[], types?: FileIconType[] } } = { +export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: FileIconType[] } } = { accdb: { extensions: ['accdb', 'mdb'], }, @@ -271,13 +271,13 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[], types?: extensions: ['design'], }, desktopfolder: { - types: [FileIconType.desktopFolder] + types: [FileIconType.desktopFolder], }, - docset: { - types: [FileIconType.docset] + docset: { + types: [FileIconType.docset], }, - documentsfolder: { - types: [FileIconType.documentsFolder] + documentsfolder: { + types: [FileIconType.documentsFolder], }, docx: { extensions: ['doc', 'docm', 'docx', 'docb'], @@ -292,16 +292,16 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[], types?: extensions: ['application', 'appref-ms', 'apk', 'app', 'appx', 'exe', 'ipa', 'msi', 'xap'], }, folder: { - types: [FileIconType.folder] + types: [FileIconType.folder], }, font: { extensions: ['ttf', 'otf', 'woff'], }, form: { - types: [FileIconType.form] + types: [FileIconType.form], }, genericfile: { - types: [FileIconType.genericFile] + types: [FileIconType.genericFile], }, html: { extensions: ['htm', 'html', 'mht', 'mhtml'], @@ -313,22 +313,22 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[], types?: extensions: ['lnk', 'link', 'url', 'website', 'webloc'], }, linkedfolder: { - types: [FileIconType.linkedFolder] + types: [FileIconType.linkedFolder], }, - listitem: { - types: [FileIconType.listItem] + listitem: { + types: [FileIconType.listItem], }, loop: { extensions: ['fluid', 'loop', 'note'], }, loopworkspace: { - types: [FileIconType.loopworkspace] + types: [FileIconType.loopworkspace], }, officescript: { extensions: ['osts'], }, splist: { - types: [FileIconType.list] + types: [FileIconType.list], }, mcworld: { extensions: ['mcworld'], @@ -373,7 +373,7 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[], types?: extensions: ['mpt'], }, multiple: { - types: [FileIconType.multiple] + types: [FileIconType.multiple], }, one: { // This is a partial OneNote page or section export. Not whole notebooks, see "onetoc" @@ -432,13 +432,13 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[], types?: }, photo360: {}, picturesfolder: { - types: [FileIconType.picturesFolder] + types: [FileIconType.picturesFolder], }, planner: { - types: [FileIconType.planner] + types: [FileIconType.planner], }, portfolio: { - types: [FileIconType.portfolio] + types: [FileIconType.portfolio], }, potx: { extensions: ['pot', 'potm', 'potx'], @@ -462,7 +462,7 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[], types?: extensions: ['aspx'], }, sponews: { - types: [FileIconType.news] + types: [FileIconType.news], }, spreadsheet: { extensions: ['odc', 'ods', 'gsheet', 'numbers', 'tsv'], @@ -470,14 +470,14 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[], types?: rtf: { extensions: ['epub', 'gdoc', 'odt', 'rtf', 'wri', 'pages'], }, - sharedfolder: { - types: [FileIconType.sharedFolder] + sharedfolder: { + types: [FileIconType.sharedFolder], }, playlist: { - types: [FileIconType.playlist] + types: [FileIconType.playlist], }, sway: { - types: [FileIconType.sway] + types: [FileIconType.sway], }, sysfile: { extensions: [ @@ -513,7 +513,7 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[], types?: ], }, todoitem: { - types: [FileIconType.todoItem] + types: [FileIconType.todoItem], }, txt: { extensions: ['dif', 'diff', 'readme', 'out', 'plist', 'properties', 'text', 'txt'], diff --git a/packages/react-file-type-icons/src/getFileTypeIconProps.ts b/packages/react-file-type-icons/src/getFileTypeIconProps.ts index e4011e1fd33bb5..edb2a5e5287941 100644 --- a/packages/react-file-type-icons/src/getFileTypeIconProps.ts +++ b/packages/react-file-type-icons/src/getFileTypeIconProps.ts @@ -2,7 +2,7 @@ import { FileTypeIconMap } from './FileTypeIconMap'; import { FileIconType } from './FileIconType'; let _extensionToIconName: { [key: string]: string }; -let _typeToIconName: { [key:number]: string}; +let _typeToIconName: { [key: number]: string }; const GENERIC_FILE = 'genericfile'; @@ -79,9 +79,7 @@ export function getFileTypeIconNameFromExtensionOrType( // Strip periods, force lowercase. extension = extension.replace('.', '').toLowerCase(); return _extensionToIconName[extension] || GENERIC_FILE; - } else if (type) { - if (!_typeToIconName) { _typeToIconName = {}; @@ -90,7 +88,7 @@ export function getFileTypeIconNameFromExtensionOrType( if (types) { for (let i = 0; i < types.length; i++) { - _typeToIconName[types[i]] = iconName + _typeToIconName[types[i]] = iconName; } } } From 71faacdef6a857e749d4b39027d0df5f7d4387dc Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Mon, 30 Sep 2024 12:29:04 -0700 Subject: [PATCH 11/69] post-refactoring cleanup of an unused type --- packages/react-file-type-icons/src/FileIconType.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-file-type-icons/src/FileIconType.test.ts b/packages/react-file-type-icons/src/FileIconType.test.ts index 47511569bde706..3ca2190759a3dc 100644 --- a/packages/react-file-type-icons/src/FileIconType.test.ts +++ b/packages/react-file-type-icons/src/FileIconType.test.ts @@ -1,9 +1,8 @@ import { FileIconType } from './FileIconType'; -import type { FileIconTypeInput } from './FileIconType'; let allFileTypeIconValues: FileIconType | undefined; -function validateFileIconTypeValues(allowedFileTypeIconValues: FileIconTypeInput | undefined): void { +function validateFileIconTypeValues(allowedFileTypeIconValues: FileIconType | undefined): void { // The purpose of this function is to verify that the below call compiles, // which may only occur if every enum value matches its key. } From d6b0c08a619e1902a84b765bcad4da5160b696d0 Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Mon, 30 Sep 2024 12:44:41 -0700 Subject: [PATCH 12/69] fixing error reported by `lint`: error The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype guard-for-in --- .../src/getFileTypeIconProps.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/react-file-type-icons/src/getFileTypeIconProps.ts b/packages/react-file-type-icons/src/getFileTypeIconProps.ts index edb2a5e5287941..e473167d7c9fbb 100644 --- a/packages/react-file-type-icons/src/getFileTypeIconProps.ts +++ b/packages/react-file-type-icons/src/getFileTypeIconProps.ts @@ -66,11 +66,13 @@ export function getFileTypeIconNameFromExtensionOrType( _extensionToIconName = {}; for (const iconName in FileTypeIconMap) { - const extensions = FileTypeIconMap[iconName].extensions; + if (FileTypeIconMap.hasOwnProperty(iconName)) { + const extensions = FileTypeIconMap[iconName].extensions; - if (extensions) { - for (let i = 0; i < extensions.length; i++) { - _extensionToIconName[extensions[i]] = iconName; + if (extensions) { + for (let i = 0; i < extensions.length; i++) { + _extensionToIconName[extensions[i]] = iconName; + } } } } @@ -84,11 +86,13 @@ export function getFileTypeIconNameFromExtensionOrType( _typeToIconName = {}; for (const iconName in FileTypeIconMap) { - const types = FileTypeIconMap[iconName].types; + if (FileTypeIconMap.hasOwnProperty(iconName)) { + const types = FileTypeIconMap[iconName].types; - if (types) { - for (let i = 0; i < types.length; i++) { - _typeToIconName[types[i]] = iconName; + if (types) { + for (let i = 0; i < types.length; i++) { + _typeToIconName[types[i]] = iconName; + } } } } From b86c403ab9f5bea5ab9d1b8a1363a09b0da2b8fd Mon Sep 17 00:00:00 2001 From: Carlos Perez Date: Fri, 1 Nov 2024 13:15:44 -0700 Subject: [PATCH 13/69] adding album entry to filetypeicon mapping --- packages/react-file-type-icons/src/FileTypeIconMap.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-file-type-icons/src/FileTypeIconMap.ts b/packages/react-file-type-icons/src/FileTypeIconMap.ts index 723caa6aa30ec2..0c197f5217e749 100644 --- a/packages/react-file-type-icons/src/FileTypeIconMap.ts +++ b/packages/react-file-type-icons/src/FileTypeIconMap.ts @@ -12,7 +12,9 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: archive: { extensions: ['7z', 'ace', 'arc', 'arj', 'dmg', 'gz', 'iso', 'lzh', 'pkg', 'rar', 'sit', 'tgz', 'tar', 'z'], }, - album: {}, + album: { + types: [FileIconType.album], + }, audio: { extensions: [ 'aif', From a19592f9ddffee118a18dfb1683229b1b41575a7 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Sun, 23 Nov 2025 14:04:29 -0800 Subject: [PATCH 14/69] first drop of creating a Fluent v9 version of the react-file-type-icons package. VSGHCP(CS4.5) following guidance from https://github.com/microsoft/fluentui/tree/master/docs/react-v9/contributing --- .github/CODEOWNERS | 2 + README.md | 6 +- .../library/.babelrc.json | 4 + .../file-type-icons-preview/library/.swcrc | 30 + .../file-type-icons-preview/library/LICENSE | 15 + .../library/MIGRATION.md | 248 ++++++++ .../file-type-icons-preview/library/README.md | 153 +++++ .../bundle-size/FileTypeIcon.fixture.js | 10 + .../library/config/api-extractor.json | 5 + .../library/config/tests.js | 1 + .../library/docs/Spec.md | 63 ++ .../library/eslint.config.js | 5 + .../etc/file-type-icons-preview.api.md | 138 ++++ .../library/jest.config.js | 34 + .../library/package.json | 56 ++ .../library/project.json | 8 + .../library/src/FileTypeIcon.ts | 12 + .../FileTypeIcon/FileTypeIcon.test.tsx | 74 +++ .../components/FileTypeIcon/FileTypeIcon.tsx | 25 + .../FileTypeIcon/FileTypeIcon.types.ts | 52 ++ .../src/components/FileTypeIcon/index.ts | 5 + .../FileTypeIcon/renderFileTypeIcon.tsx | 15 + .../FileTypeIcon/useFileTypeIcon.ts | 57 ++ .../useFileTypeIconStyles.styles.ts | 69 ++ .../library/src/index.ts | 25 + .../library/src/testing/isConformant.ts | 15 + .../library/src/utils/FileIconType.ts | 55 ++ .../library/src/utils/FileTypeIconMap.ts | 588 ++++++++++++++++++ .../library/src/utils/getFileTypeIconProps.ts | 220 +++++++ .../library/tsconfig.json | 22 + .../library/tsconfig.lib.json | 22 + .../library/tsconfig.spec.json | 17 + .../stories/.storybook/main.js | 14 + .../stories/.storybook/preview.js | 9 + .../stories/.storybook/tsconfig.json | 10 + .../file-type-icons-preview/stories/README.md | 17 + .../stories/eslint.config.js | 12 + .../stories/package.json | 11 + .../stories/project.json | 8 + .../stories/src/.gitkeep | 0 .../FileTypeIconCommon.stories.tsx | 64 ++ .../FileTypeIconDefault.stories.tsx | 6 + .../FileTypeIcon/FileTypeIconDescription.md | 13 + .../FileTypeIconSizes.stories.tsx | 69 ++ .../FileTypeIconSpecialTypes.stories.tsx | 54 ++ .../src/FileTypeIcon/index.stories.tsx | 22 + .../stories/src/index.ts | 1 + .../stories/tsconfig.json | 22 + .../stories/tsconfig.lib.json | 10 + .../library/etc/react-shared-contexts.api.md | 3 + .../CustomStyleHooksContext.ts | 1 + tsconfig.base.all.json | 6 +- tsconfig.base.json | 4 + 53 files changed, 2403 insertions(+), 4 deletions(-) create mode 100644 packages/react-components/file-type-icons-preview/library/.babelrc.json create mode 100644 packages/react-components/file-type-icons-preview/library/.swcrc create mode 100644 packages/react-components/file-type-icons-preview/library/LICENSE create mode 100644 packages/react-components/file-type-icons-preview/library/MIGRATION.md create mode 100644 packages/react-components/file-type-icons-preview/library/README.md create mode 100644 packages/react-components/file-type-icons-preview/library/bundle-size/FileTypeIcon.fixture.js create mode 100644 packages/react-components/file-type-icons-preview/library/config/api-extractor.json create mode 100644 packages/react-components/file-type-icons-preview/library/config/tests.js create mode 100644 packages/react-components/file-type-icons-preview/library/docs/Spec.md create mode 100644 packages/react-components/file-type-icons-preview/library/eslint.config.js create mode 100644 packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md create mode 100644 packages/react-components/file-type-icons-preview/library/jest.config.js create mode 100644 packages/react-components/file-type-icons-preview/library/package.json create mode 100644 packages/react-components/file-type-icons-preview/library/project.json create mode 100644 packages/react-components/file-type-icons-preview/library/src/FileTypeIcon.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx create mode 100644 packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.tsx create mode 100644 packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/index.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/renderFileTypeIcon.tsx create mode 100644 packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIconStyles.styles.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/index.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/testing/isConformant.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/utils/FileIconType.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/utils/FileTypeIconMap.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts create mode 100644 packages/react-components/file-type-icons-preview/library/tsconfig.json create mode 100644 packages/react-components/file-type-icons-preview/library/tsconfig.lib.json create mode 100644 packages/react-components/file-type-icons-preview/library/tsconfig.spec.json create mode 100644 packages/react-components/file-type-icons-preview/stories/.storybook/main.js create mode 100644 packages/react-components/file-type-icons-preview/stories/.storybook/preview.js create mode 100644 packages/react-components/file-type-icons-preview/stories/.storybook/tsconfig.json create mode 100644 packages/react-components/file-type-icons-preview/stories/README.md create mode 100644 packages/react-components/file-type-icons-preview/stories/eslint.config.js create mode 100644 packages/react-components/file-type-icons-preview/stories/package.json create mode 100644 packages/react-components/file-type-icons-preview/stories/project.json create mode 100644 packages/react-components/file-type-icons-preview/stories/src/.gitkeep create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSpecialTypes.stories.tsx create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx create mode 100644 packages/react-components/file-type-icons-preview/stories/src/index.ts create mode 100644 packages/react-components/file-type-icons-preview/stories/tsconfig.json create mode 100644 packages/react-components/file-type-icons-preview/stories/tsconfig.lib.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d430c6d33bbbf2..4368f1d6ac6561 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -331,6 +331,8 @@ packages/react-components/component-selector-preview/library @microsoft/teams-pr packages/react-components/component-selector-preview/stories @microsoft/teams-prg packages/react-components/react-menu-grid-preview/library @microsoft/teams-prg packages/react-components/react-menu-grid-preview/stories @microsoft/teams-prg +packages/react-components/file-type-icons-preview/library @microsoft/cxe-red +packages/react-components/file-type-icons-preview/stories @microsoft/cxe-red # <%= NX-CODEOWNER-PLACEHOLDER %> # Deprecated v9 packages - exposed as part of `/unstable` api diff --git a/README.md b/README.md index f57e18143be43e..b59baadfe6d708 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,7 @@ [![Build Status](https://img.shields.io/azure-devops/build/uifabric/fabricpublic/164/master?style=flat-square)](https://dev.azure.com/uifabric/fabricpublic/_build?definitionId=164) ![GitHub contributors](https://img.shields.io/github/contributors/microsoft/fluentui?style=flat-square) ![GitHub top language](https://img.shields.io/github/languages/top/microsoft/fluentui?style=flat-square) [![Twitter Follow](https://img.shields.io/twitter/follow/fluentui?logo=x&style=flat-square)](https://twitter.com/FluentUI?ref_src=twsrc%5Etfw) -> Fluent UI React is shipping its v9 final stable release. Visit the [Fluent UI React v9 Release page on the wiki](https://github.com/microsoft/fluentui/wiki/Fluent-UI-React-v9-Release) to learn more about the upcoming release schedule. - -Fluent UI web represents a collection of utilities, React components, and Web Components for building web applications. +Fluent UI is a collection of utilities and React components for building web applications. This repo is home to 3 separate projects today. Combining Fluent UI React v9 components with Fluent UI React v8 or v0 components is possible and allows gradual migration to Fluent UI v9. @@ -27,6 +25,8 @@ The following table will help you navigate the 3 projects and understand their d > Why are there two React versions? Fluent UI v8 is still widely used. We encourage you to migrate to Fluent UI v9. See the [Migration overview](https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--docs). +For docs on how to contribute to our repo, check out the [contributing docs](https://github.com/microsoft/fluentui/tree/master/docs/react-v9/contributing) folder. + ## FluentUI Insights [Fluent UI Insights](https://docs.microsoft.com/en-us/shows/fluent-ui-insights?utm_source=github) is a series that describes the design and decisions behind the Fluent UI design system. diff --git a/packages/react-components/file-type-icons-preview/library/.babelrc.json b/packages/react-components/file-type-icons-preview/library/.babelrc.json new file mode 100644 index 00000000000000..630deaf765c49f --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/.babelrc.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../../.babelrc-v9.json", + "plugins": ["annotate-pure-calls", "@babel/transform-react-pure-annotations"] +} diff --git a/packages/react-components/file-type-icons-preview/library/.swcrc b/packages/react-components/file-type-icons-preview/library/.swcrc new file mode 100644 index 00000000000000..b4ffa86dee3067 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/.swcrc @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "exclude": [ + "/testing", + "/**/*.cy.ts", + "/**/*.cy.tsx", + "/**/*.spec.ts", + "/**/*.spec.tsx", + "/**/*.test.ts", + "/**/*.test.tsx" + ], + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": true, + "decorators": false, + "dynamicImport": false + }, + "externalHelpers": true, + "transform": { + "react": { + "runtime": "classic", + "useSpread": true + } + }, + "target": "es2019" + }, + "minify": false, + "sourceMaps": true +} diff --git a/packages/react-components/file-type-icons-preview/library/LICENSE b/packages/react-components/file-type-icons-preview/library/LICENSE new file mode 100644 index 00000000000000..ebc6d4c8e5118f --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/LICENSE @@ -0,0 +1,15 @@ +@fluentui/file-type-icons-preview + +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Note: Usage of the fonts and icons referenced in Fluent UI React is subject to the terms listed at https://aka.ms/fluentui-assets-license diff --git a/packages/react-components/file-type-icons-preview/library/MIGRATION.md b/packages/react-components/file-type-icons-preview/library/MIGRATION.md new file mode 100644 index 00000000000000..c99d72f0caa5ce --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/MIGRATION.md @@ -0,0 +1,248 @@ +# Migration Guide: react-file-type-icons to react-file-type-icons-preview + +This guide helps you migrate from `@fluentui/react-file-type-icons` (v8) to `@fluentui/react-file-type-icons-preview` (v9). + +## Overview + +The v9 version provides a React component-based API instead of utility functions, following Fluent UI v9 patterns and conventions. + +## Package Installation + +```bash +# Remove the v8 package +npm uninstall @fluentui/react-file-type-icons + +# Install the v9 preview package +npm install @fluentui/react-file-type-icons-preview +``` + +## Breaking Changes + +### 1. Component-based API instead of utility functions + +**v8 (utility-based):** + +```tsx +import { getFileTypeIconProps, FileIconType } from '@fluentui/react-file-type-icons'; + +const iconProps = getFileTypeIconProps({ extension: 'docx', size: 48 }); +Document; +``` + +**v9 (component-based):** + +```tsx +import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; + +; +``` + +### 2. Simplified Props + +The v9 component automatically handles icon name resolution and URL construction internally. + +**v8:** + +```tsx +const iconProps = getFileTypeIconProps({ + extension: 'docx', + size: 48, + imageFileType: 'png', +}); +// Manually construct URL and set on img element +``` + +**v9:** + +```tsx + +``` + +### 3. Type Usage + +**v8:** + +```tsx +import { FileIconType, getFileTypeIconProps } from '@fluentui/react-file-type-icons'; + +const iconProps = getFileTypeIconProps({ type: FileIconType.folder }); +``` + +**v9:** + +```tsx +import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-preview'; + +; +``` + +### 4. Custom Base URL + +**v8:** + +```tsx +const iconProps = getFileTypeIconProps({ + extension: 'docx', + size: 48, +}); +const customUrl = `https://my-cdn.com/${iconProps.iconName}`; +``` + +**v9:** + +```tsx + +``` + +## Migration Examples + +### Example 1: Document Icon + +**Before (v8):** + +```tsx +import { getFileTypeIconProps } from '@fluentui/react-file-type-icons'; + +function DocumentItem({ filename }) { + const extension = filename.split('.').pop(); + const iconProps = getFileTypeIconProps({ extension, size: 32 }); + + return ( +
+ {filename} + {filename} +
+ ); +} +``` + +**After (v9):** + +```tsx +import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; + +function DocumentItem({ filename }) { + const extension = filename.split('.').pop(); + + return ( +
+ + {filename} +
+ ); +} +``` + +### Example 2: Folder Icon + +**Before (v8):** + +```tsx +import { getFileTypeIconProps, FileIconType } from '@fluentui/react-file-type-icons'; + +const folderIconProps = getFileTypeIconProps({ + type: FileIconType.folder, + size: 48, +}); + +Folder; +``` + +**After (v9):** + +```tsx +import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-preview'; + +; +``` + +### Example 3: Dynamic File List + +**Before (v8):** + +```tsx +import { getFileTypeIconProps } from '@fluentui/react-file-type-icons'; + +function FileList({ files }) { + return ( +
    + {files.map(file => { + const extension = file.name.split('.').pop(); + const iconProps = getFileTypeIconProps({ extension, size: 24 }); + + return ( +
  • + {file.name} + {file.name} +
  • + ); + })} +
+ ); +} +``` + +**After (v9):** + +```tsx +import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; + +function FileList({ files }) { + return ( +
    + {files.map(file => { + const extension = file.name.split('.').pop(); + + return ( +
  • + + {file.name} +
  • + ); + })} +
+ ); +} +``` + +## Utilities Still Available + +If you need to access the underlying utilities for custom use cases, they are still exported: + +```tsx +import { + getFileTypeIconProps, + FileIconType, + getFileTypeIconNameFromExtensionOrType, +} from '@fluentui/react-file-type-icons-preview'; + +// Use utilities if you need custom logic +const iconName = getFileTypeIconNameFromExtensionOrType({ extension: 'docx' }); +``` + +## Benefits of Migration + +1. **Simpler API**: Use a React component instead of manually handling URLs and img elements +2. **Better TypeScript**: Full type safety with v9's improved type definitions +3. **Consistent with Fluent UI v9**: Follows the same patterns as other v9 components +4. **Automatic optimization**: The component handles pixel ratio and image format internally +5. **Better accessibility**: Built-in alt text and ARIA support + +## Feature Parity + +The v9 package maintains full feature parity with v8: + +- ✅ All file type icons (100+ extensions) +- ✅ Special types (folder, genericFile, listItem, etc.) +- ✅ All sizes (16, 20, 24, 32, 40, 48, 64, 96) +- ✅ SVG and PNG support +- ✅ Custom base URL +- ✅ Device pixel ratio handling + +## Need Help? + +If you encounter issues during migration: + +1. Check the [Storybook examples](https://aka.ms/fluentui-storybook) +2. Review the [API documentation](https://aka.ms/fluentui-api) +3. Open an issue on [GitHub](https://github.com/microsoft/fluentui) diff --git a/packages/react-components/file-type-icons-preview/library/README.md b/packages/react-components/file-type-icons-preview/library/README.md new file mode 100644 index 00000000000000..81c371e34e730b --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/README.md @@ -0,0 +1,153 @@ +# @fluentui/file-type-icons-preview + +**File Type Icons components for [Fluent UI React](https://react.fluentui.dev/)** + +These are not production-ready components and **should never be used in product**. This space is useful for testing new components whose APIs might change before final release. + +## Components + +### FileTypeIcon + +The `FileTypeIcon` component displays an icon representing a file type based on its extension or a special type (like folder). It automatically selects the appropriate icon from the Fluent Design file type icon set. + +#### Features + +- 🎨 **100+ file type icons** - Supports all common file extensions +- 📏 **Multiple sizes** - 16, 20, 24, 32, 40, 48, 64, and 96 pixels +- 🖼️ **Format support** - Renders icons as SVG or PNG +- 📁 **Special types** - Folder, list items, and other non-file icons +- ♿ **Accessible** - Includes proper alt text for screen readers +- 🎯 **Automatic optimization** - Handles device pixel ratio for crisp display + +## Usage + +### Basic Usage + +```tsx +import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; + +// Display a Word document icon + + +// Display a folder icon + +``` + +### File Extensions + +The component recognizes 100+ file extensions: + +```tsx +import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; + + // Word document + // Excel spreadsheet + // PowerPoint presentation + // PDF document + // Archive file + // Video file + // Image file +``` + +### Sizes + +All standard Fluent UI icon sizes are supported: + +```tsx + + + + + + + + +``` + +### Special Types + +Use the `type` prop for non-extension-based icons: + +```tsx +import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-preview'; + + + + + + +``` + +### Image Format + +Choose between SVG (default) or PNG: + +```tsx +// SVG (default, recommended for most cases) + + +// PNG (better for certain legacy scenarios) + +``` + +### Custom CDN + +Override the default Fluent CDN with your own: + +```tsx + +``` + +### With Custom Styling + +Apply custom className or style: + +```tsx + +``` + +## Migration from v8 + +If you're migrating from `@fluentui/react-file-type-icons` (v8), see the [Migration Guide](./MIGRATION.md). + +### Quick Comparison + +**v8 (utility-based):** + +```tsx +import { getFileTypeIconProps } from '@fluentui/react-file-type-icons'; + +const iconProps = getFileTypeIconProps({ extension: 'docx', size: 48 }); +Document; +``` + +**v9 (component-based):** + +```tsx +import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; + +; +``` + +## Utilities + +The underlying utilities are also exported for advanced use cases: + +```tsx +import { + getFileTypeIconProps, + FileIconType, + getFileTypeIconNameFromExtensionOrType, + FileTypeIconMap, +} from '@fluentui/react-file-type-icons-preview'; + +// Get icon name for custom logic +const iconName = getFileTypeIconNameFromExtensionOrType({ extension: 'docx' }); + +// Get full icon props +const iconProps = getFileTypeIconProps({ extension: 'docx', size: 48 }); +``` + +## API + +See [Fluent UI Storybook](https://aka.ms/fluentui-storybook) for full API documentation and interactive examples. diff --git a/packages/react-components/file-type-icons-preview/library/bundle-size/FileTypeIcon.fixture.js b/packages/react-components/file-type-icons-preview/library/bundle-size/FileTypeIcon.fixture.js new file mode 100644 index 00000000000000..762816526dc35e --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/bundle-size/FileTypeIcon.fixture.js @@ -0,0 +1,10 @@ +import { FileTypeIcon } from './FileTypeIcon'; + +console.log(FileTypeIcon); + +export default { + name: 'FileTypeIcon', + props: { + extension: 'docx', + }, +}; diff --git a/packages/react-components/file-type-icons-preview/library/config/api-extractor.json b/packages/react-components/file-type-icons-preview/library/config/api-extractor.json new file mode 100644 index 00000000000000..8d482156d10d53 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/config/api-extractor.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "@fluentui/scripts-api-extractor/api-extractor.common.v-next.json", + "mainEntryPointFilePath": "/../../../../../../dist/out-tsc/types/packages/react-components//library/src/index.d.ts" +} diff --git a/packages/react-components/file-type-icons-preview/library/config/tests.js b/packages/react-components/file-type-icons-preview/library/config/tests.js new file mode 100644 index 00000000000000..2e211ae9e21420 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/config/tests.js @@ -0,0 +1 @@ +/** Jest test setup file. */ diff --git a/packages/react-components/file-type-icons-preview/library/docs/Spec.md b/packages/react-components/file-type-icons-preview/library/docs/Spec.md new file mode 100644 index 00000000000000..c25b450f42e8c7 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/docs/Spec.md @@ -0,0 +1,63 @@ +# @fluentui/file-type-icons-preview Spec + +## Background + +_Description and use cases of this component_ + +## Prior Art + +_Include background research done for this component_ + +- _Link to Open UI research_ +- _Link to comparison of v7 and v0_ +- _Link to GitHub epic issue for the converged component_ + +## Sample Code + +_Provide some representative example code that uses the proposed API for the component_ + +## Variants + +_Describe visual or functional variants of this control, if applicable. For example, a slider could have a 2D variant._ + +## API + +_List the **Props** and **Slots** proposed for the component. Ideally this would just be a link to the component's `.types.ts` file_ + +## Structure + +- _**Public**_ +- _**Internal**_ +- _**DOM** - how the component will be rendered as HTML elements_ + +## Migration + +_Describe what will need to be done to upgrade from the existing implementations:_ + +- _Migration from v8_ +- _Migration from v0_ + +## Behaviors + +_Explain how the component will behave in use, including:_ + +- _Component States_ +- _Interaction_ + - _Keyboard_ + - _Cursor_ + - _Touch_ + - _Screen readers_ + +## Accessibility + +Base accessibility information is included in the design document. After the spec is filled and review, outcomes from it need to be communicated to design and incorporated in the design document. + +- Decide whether to use **native element** or folow **ARIA** and provide reasons +- Identify the **[ARIA](https://www.w3.org/TR/wai-aria-practices-1.2/) pattern** and, if the component is listed there, follow its specification as possible. +- Identify accessibility **variants**, the `role` ([ARIA roles](https://www.w3.org/TR/wai-aria-1.1/#role_definitions)) of the component, its `slots` and `aria-*` props. +- Describe the **keyboard navigation**: Tab Oder and Arrow Key Navigation. Describe any other keyboard **shortcuts** used +- Specify texts for **state change announcements** - [ARIA live regions + ](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) (number of available items in dropdown, error messages, confirmations, ...) +- Identify UI parts that appear on **hover or focus** and specify keyboard and screen reader interaction with them +- List cases when **focus** needs to be **trapped** in sections of the UI (for dialogs and popups or for hierarchical navigation) +- List cases when **focus** needs to be **moved programatically** (if parts of the UI are appearing/disappearing or other cases) diff --git a/packages/react-components/file-type-icons-preview/library/eslint.config.js b/packages/react-components/file-type-icons-preview/library/eslint.config.js new file mode 100644 index 00000000000000..ec2e7cb1fc479f --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/eslint.config.js @@ -0,0 +1,5 @@ +// @ts-check + +const fluentPlugin = require('@fluentui/eslint-plugin'); + +module.exports = [...fluentPlugin.configs['flat/react']]; diff --git a/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md b/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md new file mode 100644 index 00000000000000..777b08a66f0f02 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md @@ -0,0 +1,138 @@ +## API Report File for "@fluentui/file-type-icons-preview" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { ComponentProps } from '@fluentui/react-utilities'; +import type { ComponentState } from '@fluentui/react-utilities'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import type { JSXElement } from '@fluentui/react-utilities'; +import * as React_2 from 'react'; +import type { Slot } from '@fluentui/react-utilities'; +import type { SlotClassNames } from '@fluentui/react-utilities'; + +// @public (undocumented) +export const DEFAULT_ICON_SIZE: FileTypeIconSize; + +// @public +export enum FileIconType { + // (undocumented) + album = 21,// Start at 1 so it will evaluate as "truthy" + // (undocumented) + desktopFolder = 9, + // (undocumented) + docset = 1, + // (undocumented) + documentsFolder = 10, + // (undocumented) + folder = 2, + // (undocumented) + form = 14, + // (undocumented) + genericFile = 3, + // (undocumented) + linkedFolder = 12, + // (undocumented) + list = 13, + // (undocumented) + listForm = 22, + // (undocumented) + listItem = 4, + // (undocumented) + loopworkspace = 17, + // (undocumented) + multiple = 6, + // (undocumented) + news = 8, + // (undocumented) + picturesFolder = 11, + // (undocumented) + planner = 18, + // (undocumented) + playlist = 16, + // (undocumented) + portfolio = 20, + // (undocumented) + sharedFolder = 5, + // (undocumented) + stream = 7, + // (undocumented) + sway = 15, + // (undocumented) + todoItem = 19 +} + +// @public (undocumented) +export type FileIconTypeInput = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22; + +// @public +export const FileTypeIcon: ForwardRefComponent; + +// @public (undocumented) +export const fileTypeIconClassNames: SlotClassNames; + +// @public +export const FileTypeIconMap: { + [key: string]: { + extensions?: string[]; + }; +}; + +// @public (undocumented) +export type FileTypeIconProps = ComponentProps & { + extension?: string; + type?: FileIconTypeInput; + size?: FileTypeIconSize; + imageFileType?: ImageFileType; + baseUrl?: string; +}; + +// @public (undocumented) +export type FileTypeIconSize = 16 | 20 | 24 | 32 | 40 | 48 | 64 | 96; + +// @public (undocumented) +export type FileTypeIconSlots = { + root: Slot<'img'>; +}; + +// @public (undocumented) +export type FileTypeIconState = ComponentState & Required> & { + iconUrl: string; +}; + +// @public +export function getFileTypeIconNameFromExtensionOrType(extension: string | undefined, type: FileIconType | undefined): string; + +// @public +export function getFileTypeIconProps(options: IFileTypeIconOptions): { + iconName: string; + 'aria-label'?: string; +}; + +// @public +export function getFileTypeIconSuffix(size: FileTypeIconSize, imageFileType?: ImageFileType, win?: Window): string; + +// @public (undocumented) +export interface IFileTypeIconOptions { + extension?: string; + imageFileType?: ImageFileType; + size?: FileTypeIconSize; + type?: FileIconTypeInput; +} + +// @public (undocumented) +export type ImageFileType = 'svg' | 'png'; + +// @public +export const renderFileTypeIcon_unstable: (state: FileTypeIconState) => JSXElement; + +// @public +export const useFileTypeIcon_unstable: (props: FileTypeIconProps, ref: React_2.Ref) => FileTypeIconState; + +// @public +export const useFileTypeIconStyles_unstable: (state: FileTypeIconState) => FileTypeIconState; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/react-components/file-type-icons-preview/library/jest.config.js b/packages/react-components/file-type-icons-preview/library/jest.config.js new file mode 100644 index 00000000000000..b162ec0d59415e --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/jest.config.js @@ -0,0 +1,34 @@ +// @ts-check +/* eslint-disable */ + +const { readFileSync } = require('node:fs'); +const { join } = require('node:path'); + +// Reading the SWC compilation config and remove the "exclude" +// for the test files to be compiled by SWC +const { exclude: _, ...swcJestConfig } = JSON.parse(readFileSync(join(__dirname, '.swcrc'), 'utf-8')); + +// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves. +// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude" +if (swcJestConfig.swcrc === undefined) { + swcJestConfig.swcrc = false; +} + +// Uncomment if using global setup/teardown files being transformed via swc +// https://nx.dev/packages/jest/documents/overview#global-setup/teardown-with-nx-libraries +// jest needs EsModule Interop to find the default exported setup/teardown functions +// swcJestConfig.module.noInterop = false; + +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + displayName: 'file-type-icons-preview', + preset: '../../../../jest.preset.js', + transform: { + '^.+\\.tsx?$': ['@swc/jest', swcJestConfig], + }, + coverageDirectory: './coverage', + setupFilesAfterEnv: ['./config/tests.js'], + snapshotSerializers: ['@griffel/jest-serializer'], +}; diff --git a/packages/react-components/file-type-icons-preview/library/package.json b/packages/react-components/file-type-icons-preview/library/package.json new file mode 100644 index 00000000000000..6307ead298c4fe --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/package.json @@ -0,0 +1,56 @@ +{ + "name": "@fluentui/file-type-icons-preview", + "version": "0.0.0", + "private": true, + "description": "New fluentui react package", + "main": "lib-commonjs/index.js", + "module": "lib/index.js", + "typings": "./dist/index.d.ts", + "sideEffects": false, + "files": [ + "*.md", + "dist/*.d.ts", + "lib", + "lib-commonjs" + ], + "repository": { + "type": "git", + "url": "https://github.com/microsoft/fluentui" + }, + "license": "MIT", + "devDependencies": { + "@fluentui/eslint-plugin": "*", + "@fluentui/react-conformance": "*", + "@fluentui/react-conformance-griffel": "*", + "@fluentui/scripts-api-extractor": "*" + }, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.3.3", + "@fluentui/react-shared-contexts": "^9.26.0", + "@fluentui/react-theme": "^9.2.0", + "@fluentui/react-utilities": "^9.25.4", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.9.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib/index.js", + "require": "./lib-commonjs/index.js" + }, + "./package.json": "./package.json" + }, + "beachball": { + "disallowedChangeTypes": [ + "major", + "prerelease" + ] + } +} diff --git a/packages/react-components/file-type-icons-preview/library/project.json b/packages/react-components/file-type-icons-preview/library/project.json new file mode 100644 index 00000000000000..a2facb7b2e2771 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/project.json @@ -0,0 +1,8 @@ +{ + "name": "file-type-icons-preview", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "packages/react-components/file-type-icons-preview/library/src", + "tags": ["platform:web", "vNext"], + "implicitDependencies": [] +} diff --git a/packages/react-components/file-type-icons-preview/library/src/FileTypeIcon.ts b/packages/react-components/file-type-icons-preview/library/src/FileTypeIcon.ts new file mode 100644 index 00000000000000..38044ff59d9603 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/FileTypeIcon.ts @@ -0,0 +1,12 @@ +export type { + FileTypeIconProps, + FileTypeIconSlots, + FileTypeIconState, +} from './components/FileTypeIcon/index'; +export { + FileTypeIcon, + fileTypeIconClassNames, + renderFileTypeIcon_unstable, + useFileTypeIconStyles_unstable, + useFileTypeIcon_unstable, +} from './components/FileTypeIcon/index'; diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx new file mode 100644 index 00000000000000..bd3a677bf1656c --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import { isConformant } from '../../testing/isConformant'; +import { render } from '@testing-library/react'; +import { FileTypeIcon } from './FileTypeIcon'; +import { FileIconType } from '../../utils/FileIconType'; + +describe('FileTypeIcon', () => { + isConformant({ + Component: FileTypeIcon, + displayName: 'FileTypeIcon', + }); + + it('renders with a file extension', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('docx'); + }); + + it('renders with a file type', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('folder'); + }); + + it('renders with custom size', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('48'); + }); + + it('renders with PNG image type', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('png'); + }); + + it('renders with SVG image type by default', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('svg'); + }); + + it('applies custom className', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img?.className).toContain('custom-class'); + }); + + it('renders with custom baseUrl', () => { + const customUrl = 'https://example.com/icons/'; + const result = render(); + const img = result.container.querySelector('img'); + expect(img?.src).toContain(customUrl); + }); + + it('handles file extensions with leading dot', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('pdf'); + }); + + it('defaults to genericfile for unknown extensions', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('genericfile'); + }); +}); diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.tsx b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.tsx new file mode 100644 index 00000000000000..c0b5835360fcef --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.tsx @@ -0,0 +1,25 @@ +'use client'; + +import * as React from 'react'; +import { renderFileTypeIcon_unstable } from './renderFileTypeIcon'; +import { useFileTypeIcon_unstable } from './useFileTypeIcon'; +import { useFileTypeIconStyles_unstable } from './useFileTypeIconStyles.styles'; +import type { FileTypeIconProps } from './FileTypeIcon.types'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts'; + +/** + * FileTypeIcon component displays an icon representing a file type based on its extension or type. + * It supports various sizes and image formats (SVG and PNG). + */ +export const FileTypeIcon: ForwardRefComponent = React.forwardRef((props, ref) => { + const state = useFileTypeIcon_unstable(props, ref); + + useFileTypeIconStyles_unstable(state); + + useCustomStyleHook_unstable('useFileTypeIconStyles_unstable')(state); + + return renderFileTypeIcon_unstable(state); +}); + +FileTypeIcon.displayName = 'FileTypeIcon'; diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts new file mode 100644 index 00000000000000..241e6d0ba9d4da --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts @@ -0,0 +1,52 @@ +import type { ComponentState, ComponentProps, Slot } from '@fluentui/react-utilities'; +import type { FileIconTypeInput } from '../../utils/FileIconType'; +import type { FileTypeIconSize, ImageFileType } from '../../utils/getFileTypeIconProps'; + +export type FileTypeIconSlots = { + /** + * The root slot, an img element that displays the file type icon. + */ + root: Slot<'img'>; +}; + +export type FileTypeIconProps = ComponentProps & { + /** + * The file extension, such as 'pptx' or '.docx', for which you need an icon. + * For file type icons that are not associated with a file extension, + * such as folder, use the type property. + */ + extension?: string; + + /** + * The type of file type icon you need. Use this property for + * file type icons that are not associated with a file extension, + * such as folder. + */ + type?: FileIconTypeInput; + + /** + * The size of the icon in pixels. + * @default 16 + */ + size?: FileTypeIconSize; + + /** + * The type of image file to use. Can be svg or png. + * @default 'svg' + */ + imageFileType?: ImageFileType; + + /** + * The base URL for the icon assets. If not provided, uses the default Fluent CDN. + * @default 'https://res-1.cdn.office.net/files/fabric-cdn-prod_20251119.001/assets/item-types/' + */ + baseUrl?: string; +}; + +export type FileTypeIconState = ComponentState & + Required> & { + /** + * The computed icon URL based on extension/type, size, and image file type. + */ + iconUrl: string; + }; diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/index.ts b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/index.ts new file mode 100644 index 00000000000000..f70e9027521bc4 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/index.ts @@ -0,0 +1,5 @@ +export { FileTypeIcon } from './FileTypeIcon'; +export type { FileTypeIconProps, FileTypeIconSlots, FileTypeIconState } from './FileTypeIcon.types'; +export { renderFileTypeIcon_unstable } from './renderFileTypeIcon'; +export { useFileTypeIcon_unstable } from './useFileTypeIcon'; +export { fileTypeIconClassNames, useFileTypeIconStyles_unstable } from './useFileTypeIconStyles.styles'; diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/renderFileTypeIcon.tsx b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/renderFileTypeIcon.tsx new file mode 100644 index 00000000000000..b0a81e57b70b58 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/renderFileTypeIcon.tsx @@ -0,0 +1,15 @@ +/** @jsxRuntime automatic */ +/** @jsxImportSource @fluentui/react-jsx-runtime */ + +import { assertSlots } from '@fluentui/react-utilities'; +import type { JSXElement } from '@fluentui/react-utilities'; +import type { FileTypeIconSlots, FileTypeIconState } from './FileTypeIcon.types'; + +/** + * Render the FileTypeIcon component. + */ +export const renderFileTypeIcon_unstable = (state: FileTypeIconState): JSXElement => { + assertSlots(state); + + return ; +}; diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts new file mode 100644 index 00000000000000..560b6c41eac1f4 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts @@ -0,0 +1,57 @@ +import * as React from 'react'; +import { getIntrinsicElementProps, slot } from '@fluentui/react-utilities'; +import type { FileTypeIconProps, FileTypeIconState } from './FileTypeIcon.types'; +import { getFileTypeIconProps, DEFAULT_ICON_SIZE } from '../../utils/getFileTypeIconProps'; + +const DEFAULT_BASE_URL = 'https://res-1.cdn.office.net/files/fabric-cdn-prod_20251119.001/assets/item-types/'; + +/** + * Returns the props and state required to render the FileTypeIcon component. + */ +export const useFileTypeIcon_unstable = ( + props: FileTypeIconProps, + ref: React.Ref, +): FileTypeIconState => { + const { + extension, + type, + size = DEFAULT_ICON_SIZE, + imageFileType = 'svg', + baseUrl = DEFAULT_BASE_URL, + } = props; + + // Get the icon name using the utility function + const iconProps = getFileTypeIconProps({ + extension, + type, + size, + imageFileType, + }); + + // Construct the full icon URL + const iconUrl = `${baseUrl}${iconProps.iconName}.${imageFileType}`; + + const state: FileTypeIconState = { + size, + imageFileType, + iconUrl, + components: { + root: 'img', + }, + root: slot.always( + getIntrinsicElementProps('img', { + ref, + src: iconUrl, + alt: iconProps['aria-label'] || 'File type icon', + ...props, + // Remove our custom props from being passed to the img element + extension: undefined, + type: undefined, + baseUrl: undefined, + }), + { elementType: 'img' }, + ), + }; + + return state; +}; diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIconStyles.styles.ts b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIconStyles.styles.ts new file mode 100644 index 00000000000000..cb3502c2a20218 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIconStyles.styles.ts @@ -0,0 +1,69 @@ +'use client'; + +import { mergeClasses, makeStyles } from '@griffel/react'; +import type { FileTypeIconSlots, FileTypeIconState } from './FileTypeIcon.types'; +import type { SlotClassNames } from '@fluentui/react-utilities'; + +export const fileTypeIconClassNames: SlotClassNames = { + root: 'fui-FileTypeIcon', +}; + +const useStyles = makeStyles({ + // Base styles + root: { + display: 'inline-block', + verticalAlign: 'middle', + }, + + // Size variations - ensure the icon displays at the correct size + size16: { + width: '16px', + height: '16px', + }, + size20: { + width: '20px', + height: '20px', + }, + size24: { + width: '24px', + height: '24px', + }, + size32: { + width: '32px', + height: '32px', + }, + size40: { + width: '40px', + height: '40px', + }, + size48: { + width: '48px', + height: '48px', + }, + size64: { + width: '64px', + height: '64px', + }, + size96: { + width: '96px', + height: '96px', + }, +}); + +/** + * Apply styling to the FileTypeIcon slots based on the state. + */ +export const useFileTypeIconStyles_unstable = (state: FileTypeIconState): FileTypeIconState => { + 'use no memo'; + + const styles = useStyles(); + + state.root.className = mergeClasses( + fileTypeIconClassNames.root, + styles.root, + styles[`size${state.size}`], + state.root.className, + ); + + return state; +}; diff --git a/packages/react-components/file-type-icons-preview/library/src/index.ts b/packages/react-components/file-type-icons-preview/library/src/index.ts new file mode 100644 index 00000000000000..591cfbed9e30c6 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/index.ts @@ -0,0 +1,25 @@ +// Component exports +export { + FileTypeIcon, + fileTypeIconClassNames, + renderFileTypeIcon_unstable, + useFileTypeIcon_unstable, + useFileTypeIconStyles_unstable, +} from './components/FileTypeIcon/index'; +export type { FileTypeIconProps, FileTypeIconSlots, FileTypeIconState } from './components/FileTypeIcon/index'; + +// Utility exports (for backward compatibility and advanced usage) +export { FileIconType } from './utils/FileIconType'; +export type { FileIconTypeInput } from './utils/FileIconType'; +export { FileTypeIconMap } from './utils/FileTypeIconMap'; +export { + getFileTypeIconProps, + getFileTypeIconNameFromExtensionOrType, + getFileTypeIconSuffix, + DEFAULT_ICON_SIZE, +} from './utils/getFileTypeIconProps'; +export type { + FileTypeIconSize, + ImageFileType, + IFileTypeIconOptions, +} from './utils/getFileTypeIconProps'; diff --git a/packages/react-components/file-type-icons-preview/library/src/testing/isConformant.ts b/packages/react-components/file-type-icons-preview/library/src/testing/isConformant.ts new file mode 100644 index 00000000000000..8ed2da0f925135 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/testing/isConformant.ts @@ -0,0 +1,15 @@ +import { isConformant as baseIsConformant } from '@fluentui/react-conformance'; +import type { IsConformantOptions, TestObject } from '@fluentui/react-conformance'; +import griffelTests from '@fluentui/react-conformance-griffel'; + +export function isConformant( + testInfo: Omit, 'componentPath'> & { componentPath?: string }, +): void { + const defaultOptions: Partial> = { + tsConfig: { configName: 'tsconfig.spec.json' }, + componentPath: require.main?.filename.replace('.test', ''), + extraTests: griffelTests as TestObject, + }; + + baseIsConformant(defaultOptions, testInfo); +} diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/FileIconType.ts b/packages/react-components/file-type-icons-preview/library/src/utils/FileIconType.ts new file mode 100644 index 00000000000000..22a7ee22bfcf81 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/utils/FileIconType.ts @@ -0,0 +1,55 @@ +/** + * Enumerates special file type icons that do not map to any file extensions. + * For example, the 'pptx' icon maps to the extensions 'ppt', 'pptm', 'pptx', + * but the 'folder' icon does not map to any extensions and should be obtained + * via this enum. + */ + +export enum FileIconType { + docset = 1, // Start at 1 so it will evaluate as "truthy" + folder = 2, + genericFile = 3, + listItem = 4, + sharedFolder = 5, + multiple = 6, + stream = 7, + news = 8, + desktopFolder = 9, + documentsFolder = 10, + picturesFolder = 11, + linkedFolder = 12, + list = 13, + form = 14, + sway = 15, + playlist = 16, + loopworkspace = 17, + planner = 18, + todoItem = 19, + portfolio = 20, + album = 21, + listForm = 22, +} + +export type FileIconTypeInput = + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17 + | 18 + | 19 + | 20 + | 21 + | 22; diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/FileTypeIconMap.ts b/packages/react-components/file-type-icons-preview/library/src/utils/FileTypeIconMap.ts new file mode 100644 index 00000000000000..a324f893e1fbf3 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/utils/FileTypeIconMap.ts @@ -0,0 +1,588 @@ +/** + * Enumeration of icon file names, and what extensions they map to. + * Please keep items alphabetical. Items without extensions may require specific logic in the code to map. + * Always use getFileTypeIconProps to get the most up-to-date icon at the right pixel density. + */ +export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { + accdb: { + extensions: ['accdb', 'mdb'], + }, + aipage: { + extensions: ['page'], + }, + archive: { + extensions: ['7z', 'ace', 'arc', 'arj', 'dmg', 'gz', 'iso', 'lzh', 'pkg', 'rar', 'sit', 'tgz', 'tar', 'z'], + }, + album: {}, + audio: { + extensions: [ + 'aif', + 'aiff', + 'aac', + 'alac', + 'amr', + 'ape', + 'au', + 'awb', + 'dct', + 'dss', + 'dvf', + 'flac', + 'gsm', + 'm4a', + 'm4p', + 'mid', + 'mmf', + 'mp3', + 'oga', + 'ra', + 'rm', + 'wav', + 'wma', + 'wv', + ], + }, + calendar: { + extensions: ['ical', 'icalendar', 'ics', 'ifb', 'vcs'], + }, + classifier: { + extensions: ['classifier'], + }, + clipchamp: { + extensions: ['clipchamp'], + }, + cliptemplate: { + extensions: ['cliptemplate'], + }, + code: { + extensions: [ + 'abap', + 'ada', + 'adp', + 'ahk', + 'as', + 'as3', + 'asc', + 'ascx', + 'asm', + 'asp', + 'awk', + 'bash', + 'bash_login', + 'bash_logout', + 'bash_profile', + 'bashrc', + 'bat', + 'bib', + 'bsh', + 'build', + 'builder', + 'c', + 'cbl', + 'c++', + 'capfile', + 'cc', + 'cfc', + 'cfm', + 'cfml', + 'cl', + 'clj', + 'cls', + 'cmake', + 'cmd', + 'coffee', + 'config', + 'cpp', + 'cpt', + 'cpy', + 'cs', + 'cshtml', + 'cson', + 'csproj', + 'css', + 'ctp', + 'cxx', + 'd', + 'ddl', + 'di', + 'disco', + 'dml', + 'dtd', + 'dtml', + 'el', + 'emakefile', + 'erb', + 'erl', + 'f', + 'f90', + 'f95', + 'fs', + 'fsi', + 'fsscript', + 'fsx', + 'gemfile', + 'gemspec', + 'gitconfig', + 'go', + 'groovy', + 'gvy', + 'h', + 'h++', + 'haml', + 'handlebars', + 'hbs', + 'hcp', + 'hh', + 'hpp', + 'hrl', + 'hs', + 'htc', + 'hxx', + 'idl', + 'iim', + 'inc', + 'inf', + 'ini', + 'inl', + 'ipp', + 'irbrc', + 'jade', + 'jav', + 'java', + 'js', + 'json', + 'jsp', + 'jsproj', + 'jsx', + 'l', + 'less', + 'lhs', + 'lisp', + 'log', + 'lst', + 'ltx', + 'lua', + 'm', + 'mak', + 'make', + 'manifest', + 'master', + 'md', + 'markdn', + 'markdown', + 'mdown', + 'mkdn', + 'ml', + 'mli', + 'mll', + 'mly', + 'mm', + 'mud', + 'nfo', + 'opml', + 'osascript', + 'p', + 'pas', + 'patch', + 'php', + 'php2', + 'php3', + 'php4', + 'php5', + 'phtml', + 'pl', + 'pm', + 'pod', + 'pp', + 'profile', + 'ps1', + 'ps1xml', + 'psd1', + 'psm1', + 'pss', + 'pt', + 'py', + 'pyw', + 'r', + 'rake', + 'rb', + 'rbx', + 'rc', + 'rdf', + 're', + 'reg', + 'rest', + 'resw', + 'resx', + 'rhtml', + 'rjs', + 'rprofile', + 'rpy', + 'rss', + 'rst', + 'ruby', + 'rxml', + 's', + 'sass', + 'scala', + 'scm', + 'sconscript', + 'sconstruct', + 'script', + 'scss', + 'sgml', + 'sh', + 'shtml', + 'sml', + 'svn-base', + 'swift', + 'sql', + 'sty', + 'tcl', + 'tex', + 'textile', + 'tld', + 'tli', + 'tmpl', + 'tpl', + 'vb', + 'vi', + 'vim', + 'vmg', + 'webpart', + 'wsp', + 'wsdl', + 'xhtml', + 'xoml', + 'xsd', + 'xslt', + 'yaml', + 'yaws', + 'yml', + 'zsh', + ], + }, + contact: { + extensions: ['vcf'], + }, + copilot: { + extensions: ['copilot'], + }, + /* css: {}, not broken out yet, snapping to 'code' for now */ + csv: { + extensions: ['csv'], + }, + designer: { + extensions: ['design'], + }, + desktopfolder: {}, + docset: {}, + documentsfolder: {}, + docx: { + extensions: ['doc', 'docm', 'docx', 'docb'], + }, + dotx: { + extensions: ['dot', 'dotm', 'dotx'], + }, + email: { + extensions: ['eml', 'msg', 'oft', 'ost', 'pst'], + }, + exe: { + extensions: ['application', 'appref-ms', 'apk', 'app', 'appx', 'exe', 'ipa', 'msi', 'xap'], + }, + favoritesfolder: {}, + folder: {}, + font: { + extensions: ['ttf', 'otf', 'woff'], + }, + form: {}, + genericfile: {}, + html: { + extensions: ['htm', 'html', 'mht', 'mhtml'], + }, + ipynb: { + extensions: ['nnb', 'ipynb'], + }, + link: { + extensions: ['lnk', 'link', 'url', 'website', 'webloc'], + }, + linkedfolder: {}, + listform: {}, + listitem: {}, + loop: { + extensions: ['fluid', 'loop', 'note'], + }, + loopworkspace: {}, + officescript: { + extensions: ['osts'], + }, + splist: {}, + mcworld: { + extensions: ['mcworld'], + }, + mctemplate: { + extensions: ['mctemplate'], + }, + model: { + extensions: [ + '3ds', + '3mf', + 'blend', + 'cool', + 'dae', + 'df', + 'dwfx', + 'dwg', + 'dxf', + 'fbx', + 'glb', + 'gltf', + 'holo', + 'layer', + 'layout', + 'max', + 'mtl', + 'obj', + 'off', + 'ply', + 'skp', + 'stp', + 'stl', + 't', + 'thl', + 'x', + ], + }, + mpp: { + extensions: ['mpp'], + }, + mpt: { + extensions: ['mpt'], + }, + multiple: {}, + one: { + // This is a partial OneNote page or section export. Not whole notebooks, see "onetoc" + extensions: ['one', 'onepart'], + }, + onepage: { + extensions: ['onepage'], + }, + onetoc: { + // This is an entire OneNote notebook. + extensions: ['ms-one-stub', 'onetoc', 'onetoc2', 'onepkg'], // This represents a complete, logical notebook. + }, + pbiapp: {}, + pdf: { + extensions: ['pdf'], + }, + photo: { + extensions: [ + 'arw', + 'bmp', + 'cr2', + 'crw', + 'dic', + 'dicm', + 'dcm', + 'dcm30', + 'dcr', + 'dds', + 'dib', + 'dng', + 'erf', + 'gif', + 'heic', + 'heif', + 'ico', + 'jfi', + 'jfif', + 'jif', + 'jpe', + 'jpeg', + 'jpg', + 'jxr', + 'kdc', + 'mrw', + 'nef', + 'orf', + 'pct', + 'pict', + 'png', + 'pns', + 'psb', + 'psd', + 'raw', + 'tga', + 'tif', + 'tiff', + 'wdp', + ], + }, + photo360: {}, + picturesfolder: {}, + planner: {}, + portfolio: {}, + potx: { + extensions: ['pot', 'potm', 'potx'], + }, + powerbi: { + extensions: ['pbids', 'pbix'], + }, + ppsx: { + extensions: ['pps', 'ppsm', 'ppsx'], + }, + pptx: { + extensions: ['ppt', 'pptm', 'pptx', 'sldx', 'sldm'], + }, + presentation: { + extensions: ['odp', 'gslides', 'key'], + }, + pub: { + extensions: ['pub'], + }, + spo: { + extensions: ['aspx'], + }, + sponews: {}, + spreadsheet: { + extensions: ['odc', 'ods', 'gsheet', 'numbers', 'tsv'], + }, + rtf: { + extensions: ['epub', 'gdoc', 'odt', 'rtf', 'wri', 'pages'], + }, + sharedfolder: {}, + playlist: {}, + sway: {}, + sysfile: { + extensions: [ + 'bak', + 'bin', + 'cab', + 'cache', + 'cat', + 'cer', + 'class', + 'dat', + 'db', + 'dbg', + 'dl_', + 'dll', + 'ithmb', + 'jar', + 'kb', + 'ldt', + 'lrprev', + 'pkpass', + 'ppa', + 'ppam', + 'pdb', + 'rom', + 'thm', + 'thmx', + 'vsl', + 'xla', + 'xlam', + 'xlb', + 'xll', + ], + }, + todoitem: {}, + txt: { + extensions: ['dif', 'diff', 'readme', 'out', 'plist', 'properties', 'text', 'txt'], + }, + vaultclosed: {}, + vaultopen: {}, + vector: { + extensions: [ + 'ai', + 'ait', + 'cvs', + 'dgn', + 'gdraw', + 'pd', + 'emf', + 'eps', + 'fig', + 'ind', + 'indd', + 'indl', + 'indt', + 'indb', + 'ps', + 'svg', + 'svgz', + 'wmf', + 'oxps', + 'xps', + 'xd', + 'sketch', + ], + }, + video: { + extensions: [ + '3g2', + '3gp', + '3gp2', + '3gpp', + 'asf', + 'avi', + 'dvr-ms', + 'flv', + 'm1v', + 'm4v', + 'mkv', + 'mod', + 'mov', + 'mm4p', + 'mp2', + 'mp2v', + 'mp4', + 'mp4v', + 'mpa', + 'mpe', + 'mpeg', + 'mpg', + 'mpv', + 'mpv2', + 'mts', + 'ogg', + 'qt', + 'swf', + 'ts', + 'vob', + 'webm', + 'wlmp', + 'wm', + 'wmv', + 'wmx', + ], + }, + video360: {}, + vsdx: { + extensions: ['vdx', 'vsd', 'vsdm', 'vsdx', 'vsw', 'vdw'], + }, + vssx: { + extensions: ['vss', 'vssm', 'vssx'], + }, + vstx: { + extensions: ['vst', 'vstm', 'vstx', 'vsx'], + }, + whiteboard: { + extensions: ['whiteboard', 'wbtx'], + }, + xlsx: { + extensions: ['xlc', 'xls', 'xlsb', 'xlsm', 'xlsx', 'xlw'], + }, + xltx: { + extensions: ['xlt', 'xltm', 'xltx'], + }, + xml: { + extensions: ['xaml', 'xml', 'xsl'], + }, + xsn: { + extensions: ['xsn'], + }, + zip: { + extensions: ['zip'], + }, +}; diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts new file mode 100644 index 00000000000000..3c69469e2587cf --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts @@ -0,0 +1,220 @@ +import { FileTypeIconMap } from './FileTypeIconMap'; +import { FileIconType } from './FileIconType'; +import type { FileIconTypeInput } from './FileIconType'; + +let _extensionToIconName: { [key: string]: string }; + +const GENERIC_FILE = 'genericfile'; +const FOLDER = 'folder'; +const SHARED_FOLDER = 'sharedfolder'; +const DOCSET_FOLDER = 'docset'; +const LIST_ITEM = 'listitem'; +const LIST = 'splist'; +const MULTIPLE_ITEMS = 'multiple'; +const NEWS = 'sponews'; +const STREAM = 'video'; +const DESKTOP_FOLDER = 'desktopfolder'; +const DOCUMENTS_FOLDER = 'documentsfolder'; +const PICTURES_FOLDER = 'picturesfolder'; +const LINKED_FOLDER = 'linkedfolder'; +const FORM = 'form'; +const SWAY = 'sway'; +const PLAYLIST = 'playlist'; +const LOOP_WORKSPACE = 'loopworkspace'; +const TODOITEM = 'todoitem'; +const PLANNER = 'planner'; +const PORTFOLIO = 'portfolio'; +const ALBUM = 'album'; +const LIST_FORM = 'listform'; + +export const DEFAULT_ICON_SIZE: FileTypeIconSize = 16; +export type FileTypeIconSize = 16 | 20 | 24 | 32 | 40 | 48 | 64 | 96; +export type ImageFileType = 'svg' | 'png'; + +export interface IFileTypeIconOptions { + /** + * The file extension, such as .pptx, for which you need an icon. + * For file type icons that are not associated with a file + * extension, such as folder, use the type property. + */ + extension?: string; + /** + * The type of file type icon you need. Use this property for + * file type icons that are not associated with a file extension, + * such as folder. + */ + type?: FileIconTypeInput; + /** + * The size of the icon in pixels. + * @default 16 + */ + size?: FileTypeIconSize; + /** + * The type of image file to use. Can be svg or png. + * @default 'svg' + */ + imageFileType?: ImageFileType; +} + +/** + * This function returns properties for a file type icon given the IFileTypeIconOptions. + * It accounts for different device pixel ratios. For example, + * `getFileTypeIconProps({ extension: 'doc', size: 16, imageFileType: 'png' })` + * will return `{ iconName: 'docx16_2x_png' }` if the `devicePixelRatio` is 2. + * @param options - Configuration options for the file type icon + */ +export function getFileTypeIconProps(options: IFileTypeIconOptions): { iconName: string; 'aria-label'?: string } { + // First, obtain the base name of the icon using the extension or type. + const { extension, type, size, imageFileType } = options; + + const iconBaseName = getFileTypeIconNameFromExtensionOrType(extension, type); + + // Next, obtain the suffix using the icon size, user's device pixel ratio, and + // preference for svg or png + const _size: FileTypeIconSize = size || DEFAULT_ICON_SIZE; + const suffix: string = getFileTypeIconSuffix(_size, imageFileType); + + return { iconName: iconBaseName + suffix, 'aria-label': extension }; +} + +/** + * Gets the base icon name from a file extension or file icon type. + * @param extension - The file extension (e.g., 'docx', 'pdf') + * @param type - The file icon type for non-extension icons + * @returns The base icon name + */ +export function getFileTypeIconNameFromExtensionOrType( + extension: string | undefined, + type: FileIconType | undefined, +): string { + let iconBaseName: string | undefined; + if (extension) { + if (!_extensionToIconName) { + _extensionToIconName = {}; + + for (const iconName in FileTypeIconMap) { + if (FileTypeIconMap.hasOwnProperty(iconName)) { + const extensions = FileTypeIconMap[iconName].extensions; + + if (extensions) { + for (let i = 0; i < extensions.length; i++) { + _extensionToIconName[extensions[i]] = iconName; + } + } + } + } + } + + // Strip periods, force lowercase. + extension = extension.replace('.', '').toLowerCase(); + return _extensionToIconName[extension] || GENERIC_FILE; + } else if (type) { + switch (type) { + case FileIconType.docset: + iconBaseName = DOCSET_FOLDER; + break; + case FileIconType.folder: + iconBaseName = FOLDER; + break; + case FileIconType.listItem: + iconBaseName = LIST_ITEM; + break; + case FileIconType.sharedFolder: + iconBaseName = SHARED_FOLDER; + break; + case FileIconType.stream: + iconBaseName = STREAM; + break; + case FileIconType.multiple: + iconBaseName = MULTIPLE_ITEMS; + break; + case FileIconType.news: + iconBaseName = NEWS; + break; + case FileIconType.desktopFolder: + iconBaseName = DESKTOP_FOLDER; + break; + case FileIconType.documentsFolder: + iconBaseName = DOCUMENTS_FOLDER; + break; + case FileIconType.picturesFolder: + iconBaseName = PICTURES_FOLDER; + break; + case FileIconType.linkedFolder: + iconBaseName = LINKED_FOLDER; + break; + case FileIconType.list: + iconBaseName = LIST; + break; + case FileIconType.form: + iconBaseName = FORM; + break; + case FileIconType.sway: + iconBaseName = SWAY; + break; + case FileIconType.playlist: + iconBaseName = PLAYLIST; + break; + case FileIconType.loopworkspace: + iconBaseName = LOOP_WORKSPACE; + break; + case FileIconType.planner: + iconBaseName = PLANNER; + break; + case FileIconType.todoItem: + iconBaseName = TODOITEM; + break; + case FileIconType.portfolio: + iconBaseName = PORTFOLIO; + break; + case FileIconType.album: + iconBaseName = ALBUM; + break; + case FileIconType.listForm: + iconBaseName = LIST_FORM; + break; + } + } + return iconBaseName || GENERIC_FILE; +} + +/** + * Gets the suffix for the icon name based on size, file type, and device pixel ratio. + * @param size - The icon size in pixels + * @param imageFileType - The image file type ('svg' or 'png') + * @param win - Optional window object for testing + * @returns The icon name suffix + */ +export function getFileTypeIconSuffix( + size: FileTypeIconSize, + imageFileType: ImageFileType = 'svg', + win?: Window, +): string { + // eslint-disable-next-line no-restricted-globals + win ??= window; + const devicePixelRatio: number = win.devicePixelRatio; + let devicePixelRatioSuffix = ''; // Default is 1x + + // SVGs scale well, so you can generally use the default image. + // 1.5x is a special case where SVGs need a different image. + if (imageFileType === 'svg' && devicePixelRatio > 1 && devicePixelRatio <= 1.5) { + // Currently missing 1.5x SVGs at size 20, snap to 1x for now + if (size !== 20) { + devicePixelRatioSuffix = '_1.5x'; + } + } else if (imageFileType === 'png') { + // To look good, PNGs should use a different image for higher device pixel ratios + if (devicePixelRatio > 1 && devicePixelRatio <= 1.5) { + // Currently missing 1.5x icons for size 20, snap to 2x for now + devicePixelRatioSuffix = size === 20 ? '_2x' : '_1.5x'; + } else if (devicePixelRatio > 1.5 && devicePixelRatio <= 2) { + devicePixelRatioSuffix = '_2x'; + } else if (devicePixelRatio > 2 && devicePixelRatio <= 3) { + devicePixelRatioSuffix = '_3x'; + } else if (devicePixelRatio > 3) { + devicePixelRatioSuffix = '_4x'; + } + } + + return size + devicePixelRatioSuffix + '_' + imageFileType; +} diff --git a/packages/react-components/file-type-icons-preview/library/tsconfig.json b/packages/react-components/file-type-icons-preview/library/tsconfig.json new file mode 100644 index 00000000000000..32bdbdf1ac26f0 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2019", + "noEmit": true, + "isolatedModules": true, + "importHelpers": true, + "jsx": "react", + "noUnusedLocals": true, + "preserveConstEnums": true + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/packages/react-components/file-type-icons-preview/library/tsconfig.lib.json b/packages/react-components/file-type-icons-preview/library/tsconfig.lib.json new file mode 100644 index 00000000000000..53066fdd11fff0 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/tsconfig.lib.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "lib": ["ES2019", "dom"], + "declaration": true, + "declarationDir": "../../../../dist/out-tsc/types", + "outDir": "../../../../dist/out-tsc", + "inlineSources": true, + "types": ["static-assets", "environment"] + }, + "exclude": [ + "./src/testing/**", + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.stories.ts", + "**/*.stories.tsx" + ], + "include": ["./src/**/*.ts", "./src/**/*.tsx"] +} diff --git a/packages/react-components/file-type-icons-preview/library/tsconfig.spec.json b/packages/react-components/file-type-icons-preview/library/tsconfig.spec.json new file mode 100644 index 00000000000000..911456fe4b4d91 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.d.ts", + "./src/testing/**/*.ts", + "./src/testing/**/*.tsx" + ] +} diff --git a/packages/react-components/file-type-icons-preview/stories/.storybook/main.js b/packages/react-components/file-type-icons-preview/stories/.storybook/main.js new file mode 100644 index 00000000000000..67905c6bfe15f2 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/.storybook/main.js @@ -0,0 +1,14 @@ +const rootMain = require('../../../../../.storybook/main'); + +module.exports = /** @type {Omit} */ ({ + ...rootMain, + stories: [...rootMain.stories, '../src/**/*.mdx', '../src/**/index.stories.@(ts|tsx)'], + addons: [...rootMain.addons], + webpackFinal: (config, options) => { + const localConfig = { ...rootMain.webpackFinal(config, options) }; + + // add your own webpack tweaks if needed + + return localConfig; + }, +}); diff --git a/packages/react-components/file-type-icons-preview/stories/.storybook/preview.js b/packages/react-components/file-type-icons-preview/stories/.storybook/preview.js new file mode 100644 index 00000000000000..98274ed0b8095f --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/.storybook/preview.js @@ -0,0 +1,9 @@ +import * as rootPreview from '../../../../../.storybook/preview'; + +/** @type {typeof rootPreview.decorators} */ +export const decorators = [...rootPreview.decorators]; + +/** @type {typeof rootPreview.parameters} */ +export const parameters = { ...rootPreview.parameters }; + +export const tags = ['autodocs']; diff --git a/packages/react-components/file-type-icons-preview/stories/.storybook/tsconfig.json b/packages/react-components/file-type-icons-preview/stories/.storybook/tsconfig.json new file mode 100644 index 00000000000000..4cdd1ce9d006f1 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/.storybook/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "", + "allowJs": true, + "checkJs": true, + "types": ["static-assets", "environment"] + }, + "include": ["*.js"] +} diff --git a/packages/react-components/file-type-icons-preview/stories/README.md b/packages/react-components/file-type-icons-preview/stories/README.md new file mode 100644 index 00000000000000..ee82402c29b63a --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/README.md @@ -0,0 +1,17 @@ +# @fluentui/file-type-icons-preview-stories + +Storybook stories for packages/react-components/file-type-icons-preview + +## Usage + +To include within storybook specify stories globs: + +\`\`\`js +module.exports = { +stories: ['../packages/react-components/file-type-icons-preview/stories/src/**/*.mdx', '../packages/react-components/file-type-icons-preview/stories/src/**/index.stories.@(ts|tsx)'], +} +\`\`\` + +## API + +no public API available diff --git a/packages/react-components/file-type-icons-preview/stories/eslint.config.js b/packages/react-components/file-type-icons-preview/stories/eslint.config.js new file mode 100644 index 00000000000000..8d88b5e748085d --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/eslint.config.js @@ -0,0 +1,12 @@ +// @ts-check + +const fluentPlugin = require('@fluentui/eslint-plugin'); + +module.exports = [ + ...fluentPlugin.configs['flat/react'], + { + rules: { + 'import/no-extraneous-dependencies': ['error', { packageDir: ['.', '../../../../'] }], + }, + }, +]; diff --git a/packages/react-components/file-type-icons-preview/stories/package.json b/packages/react-components/file-type-icons-preview/stories/package.json new file mode 100644 index 00000000000000..bc589fd28cb512 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/package.json @@ -0,0 +1,11 @@ +{ + "name": "@fluentui/file-type-icons-preview-stories", + "version": "0.0.0", + "private": true, + "devDependencies": { + "@fluentui/react-storybook-addon": "*", + "@fluentui/react-storybook-addon-export-to-sandbox": "*", + "@fluentui/scripts-storybook": "*", + "@fluentui/eslint-plugin": "*" + } +} diff --git a/packages/react-components/file-type-icons-preview/stories/project.json b/packages/react-components/file-type-icons-preview/stories/project.json new file mode 100644 index 00000000000000..8a5f776b3250ea --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/project.json @@ -0,0 +1,8 @@ +{ + "name": "file-type-icons-preview-stories", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "packages/react-components/file-type-icons-preview/stories/src", + "tags": ["vNext", "platform:web", "type:stories"], + "implicitDependencies": [] +} diff --git a/packages/react-components/file-type-icons-preview/stories/src/.gitkeep b/packages/react-components/file-type-icons-preview/stories/src/.gitkeep new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx new file mode 100644 index 00000000000000..6c05d0b3d4bf64 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx @@ -0,0 +1,64 @@ +import * as React from 'react'; +import type { JSXElement } from '@fluentui/react-components'; +import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import { makeStyles } from '@griffel/react'; + +const useStyles = makeStyles({ + container: { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))', + gap: '16px', + }, + item: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: '8px', + }, + label: { + fontSize: '12px', + textAlign: 'center', + }, +}); + +const commonExtensions = [ + 'docx', + 'xlsx', + 'pptx', + 'pdf', + 'txt', + 'zip', + 'png', + 'jpg', + 'mp4', + 'mp3', + 'html', + 'css', + 'js', + 'json', + 'xml', + 'csv', +]; + +export const CommonFileTypes = (): JSXElement => { + const styles = useStyles(); + + return ( +
+ {commonExtensions.map(ext => ( +
+ +
.{ext}
+
+ ))} +
+ ); +}; + +CommonFileTypes.parameters = { + docs: { + description: { + story: 'FileTypeIcon displays different icons for various common file types and extensions.', + }, + }, +}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx new file mode 100644 index 00000000000000..ae1ba2eed2f3c2 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import type { JSXElement } from '@fluentui/react-components'; +import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import type { FileTypeIconProps } from '@fluentui/file-type-icons-preview'; + +export const Default = (props: Partial): JSXElement => ; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md new file mode 100644 index 00000000000000..c8ed40f13b3c93 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md @@ -0,0 +1,13 @@ +# FileTypeIcon + +FileTypeIcon displays an icon representing a file type based on its extension or a special type (like folder). + +The component automatically selects the appropriate icon from the Fluent Design file type icon set and handles device pixel ratio for optimal display quality. + +## Features + +- **Automatic icon selection**: Matches file extensions to the appropriate icon +- **Multiple sizes**: Supports 16, 20, 24, 32, 40, 48, 64, and 96 pixel sizes +- **Format support**: Renders icons as SVG or PNG +- **Special types**: Supports non-file-based icons like folders, list items, etc. +- **Accessibility**: Includes appropriate alt text for screen readers diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx new file mode 100644 index 00000000000000..1b443b1593327c --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx @@ -0,0 +1,69 @@ +import * as React from 'react'; +import type { JSXElement } from '@fluentui/react-components'; +import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import { makeStyles } from '@griffel/react'; + +const useStyles = makeStyles({ + container: { + display: 'flex', + flexDirection: 'column', + gap: '16px', + }, + row: { + display: 'flex', + alignItems: 'center', + gap: '8px', + }, + label: { + width: '100px', + }, +}); + +export const Sizes = (): JSXElement => { + const styles = useStyles(); + + return ( +
+
+
16px:
+ +
+
+
20px:
+ +
+
+
24px:
+ +
+
+
32px:
+ +
+
+
40px:
+ +
+
+
48px:
+ +
+
+
64px:
+ +
+
+
96px:
+ +
+
+ ); +}; + +Sizes.parameters = { + docs: { + description: { + story: 'FileTypeIcon supports multiple sizes: 16, 20, 24, 32, 40, 48, 64, and 96 pixels.', + }, + }, +}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSpecialTypes.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSpecialTypes.stories.tsx new file mode 100644 index 00000000000000..cb8433b1860928 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSpecialTypes.stories.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import type { JSXElement } from '@fluentui/react-components'; +import { FileTypeIcon, FileIconType } from '@fluentui/file-type-icons-preview'; +import { makeStyles } from '@griffel/react'; + +const useStyles = makeStyles({ + container: { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))', + gap: '16px', + }, + item: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: '8px', + }, + label: { + fontSize: '12px', + textAlign: 'center', + }, +}); + +const specialTypes = [ + { type: FileIconType.folder, label: 'Folder' }, + { type: FileIconType.genericFile, label: 'Generic File' }, + { type: FileIconType.sharedFolder, label: 'Shared Folder' }, + { type: FileIconType.listItem, label: 'List Item' }, + { type: FileIconType.docset, label: 'Docset' }, +]; + +export const SpecialTypes = (): JSXElement => { + const styles = useStyles(); + + return ( +
+ {specialTypes.map(({ type, label }) => ( +
+ +
{label}
+
+ ))} +
+ ); +}; + +SpecialTypes.parameters = { + docs: { + description: { + story: + 'FileTypeIcon supports special file types that are not based on file extensions, such as folders and list items.', + }, + }, +}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx new file mode 100644 index 00000000000000..04b09445521579 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import type { Meta } from '@storybook/react'; + +export { Default } from './FileTypeIconDefault.stories'; +export { Sizes } from './FileTypeIconSizes.stories'; +export { CommonFileTypes } from './FileTypeIconCommon.stories'; +export { SpecialTypes } from './FileTypeIconSpecialTypes.stories'; + +import descriptionMd from './FileTypeIconDescription.md'; + +export default { + title: 'Preview Components/FileTypeIcon', + component: FileTypeIcon, + parameters: { + docs: { + description: { + component: descriptionMd, + }, + }, + }, +} as Meta; diff --git a/packages/react-components/file-type-icons-preview/stories/src/index.ts b/packages/react-components/file-type-icons-preview/stories/src/index.ts new file mode 100644 index 00000000000000..cb0ff5c3b541f6 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/react-components/file-type-icons-preview/stories/tsconfig.json b/packages/react-components/file-type-icons-preview/stories/tsconfig.json new file mode 100644 index 00000000000000..efc50169d1df18 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2019", + "noEmit": true, + "isolatedModules": true, + "importHelpers": true, + "jsx": "react", + "noUnusedLocals": true, + "preserveConstEnums": true + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./.storybook/tsconfig.json" + } + ] +} diff --git a/packages/react-components/file-type-icons-preview/stories/tsconfig.lib.json b/packages/react-components/file-type-icons-preview/stories/tsconfig.lib.json new file mode 100644 index 00000000000000..9486b224643d9f --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "lib": ["ES2019", "dom"], + "outDir": "../../../../dist/out-tsc", + "inlineSources": true, + "types": ["static-assets", "environment"] + }, + "include": ["./src/**/*.ts", "./src/**/*.tsx"] +} diff --git a/packages/react-components/react-shared-contexts/library/etc/react-shared-contexts.api.md b/packages/react-components/react-shared-contexts/library/etc/react-shared-contexts.api.md index 32323a2d71971f..6443cf2b761338 100644 --- a/packages/react-components/react-shared-contexts/library/etc/react-shared-contexts.api.md +++ b/packages/react-components/react-shared-contexts/library/etc/react-shared-contexts.api.md @@ -97,6 +97,7 @@ export const CustomStyleHooksContext_unstable: React_2.Context Date: Sun, 23 Nov 2025 14:06:23 -0800 Subject: [PATCH 15/69] refreshing filetype icon references in other storybooks to new icon style (TO-DO: These should be updated to use getFileTypeIconProps with default CDN URL location to perma-fix) --- apps/pr-deploy-site/chiclet-test.html | 2 +- apps/vr-tests/src/stories/Tile.stories.tsx | 2 +- .../src/react-experiments/Chiclet/Chiclet.Basic.Example.tsx | 2 +- .../react-experiments/Chiclet/Chiclet.Breadcrumb.Example.tsx | 2 +- .../src/react-experiments/Chiclet/Chiclet.Xsmall.Example.tsx | 2 +- .../react-experiments/Chiclet/Chiclet.Xsmall.Footer.Example.tsx | 2 +- .../src/react-experiments/Tile/Tile.Document.Example.tsx | 2 +- .../src/react/Pickers/Picker.CustomResult.Example.tsx | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/pr-deploy-site/chiclet-test.html b/apps/pr-deploy-site/chiclet-test.html index 8d4c379d0202ad..ae9b4e3cb1a204 100644 --- a/apps/pr-deploy-site/chiclet-test.html +++ b/apps/pr-deploy-site/chiclet-test.html @@ -16,7 +16,7 @@ /> Chiclet Test Page diff --git a/apps/vr-tests/src/stories/Tile.stories.tsx b/apps/vr-tests/src/stories/Tile.stories.tsx index b0fb8a9652e943..5c56cf11000838 100644 --- a/apps/vr-tests/src/stories/Tile.stories.tsx +++ b/apps/vr-tests/src/stories/Tile.stories.tsx @@ -142,7 +142,7 @@ export const DocumentTileWithIcon = () => ( itemActivity={}>{'Test Activity'}} foreground={ = () => { diff --git a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Breadcrumb.Example.tsx b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Breadcrumb.Example.tsx index 32298d06aa8348..27c1ac3327bbb6 100644 --- a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Breadcrumb.Example.tsx +++ b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Breadcrumb.Example.tsx @@ -52,7 +52,7 @@ export class ChicletBreadcrumbExample extends React.Component { diff --git a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Example.tsx b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Example.tsx index 1a3cf023fe8e72..f915bb7d9336d6 100644 --- a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Example.tsx +++ b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Example.tsx @@ -8,7 +8,7 @@ export const ChicletXsmallExample: React.FunctionComponent<{}> = () => { diff --git a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Footer.Example.tsx b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Footer.Example.tsx index 7b10220e0a73e2..ec47f88ea8d9a7 100644 --- a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Footer.Example.tsx +++ b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Footer.Example.tsx @@ -51,7 +51,7 @@ export const ChicletXsmallFooterExample: React.FunctionComponent<{}> = () => { }>{ITEMS[3].activity}} foreground={ Date: Sun, 23 Nov 2025 15:23:06 -0800 Subject: [PATCH 16/69] more touch-ups in other places that reference the CDN url --- apps/pr-deploy-site/chiclet-test.html | 2 +- apps/vr-tests/src/stories/Tile.stories.tsx | 2 +- .../src/react-experiments/Chiclet/Chiclet.Basic.Example.tsx | 2 +- .../react-experiments/Chiclet/Chiclet.Breadcrumb.Example.tsx | 2 +- .../src/react-experiments/Chiclet/Chiclet.Xsmall.Example.tsx | 2 +- .../react-experiments/Chiclet/Chiclet.Xsmall.Footer.Example.tsx | 2 +- .../src/react-experiments/Tile/Tile.Document.Example.tsx | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/pr-deploy-site/chiclet-test.html b/apps/pr-deploy-site/chiclet-test.html index ae9b4e3cb1a204..32f79c65016e8e 100644 --- a/apps/pr-deploy-site/chiclet-test.html +++ b/apps/pr-deploy-site/chiclet-test.html @@ -16,7 +16,7 @@ /> Chiclet Test Page diff --git a/apps/vr-tests/src/stories/Tile.stories.tsx b/apps/vr-tests/src/stories/Tile.stories.tsx index 5c56cf11000838..eedce33d890865 100644 --- a/apps/vr-tests/src/stories/Tile.stories.tsx +++ b/apps/vr-tests/src/stories/Tile.stories.tsx @@ -142,7 +142,7 @@ export const DocumentTileWithIcon = () => ( itemActivity={}>{'Test Activity'}} foreground={ = () => { diff --git a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Breadcrumb.Example.tsx b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Breadcrumb.Example.tsx index 27c1ac3327bbb6..8c497b96c6d559 100644 --- a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Breadcrumb.Example.tsx +++ b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Breadcrumb.Example.tsx @@ -52,7 +52,7 @@ export class ChicletBreadcrumbExample extends React.Component { diff --git a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Example.tsx b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Example.tsx index f915bb7d9336d6..672f3543e68ba2 100644 --- a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Example.tsx +++ b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Example.tsx @@ -8,7 +8,7 @@ export const ChicletXsmallExample: React.FunctionComponent<{}> = () => { diff --git a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Footer.Example.tsx b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Footer.Example.tsx index ec47f88ea8d9a7..3040c6badad4a0 100644 --- a/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Footer.Example.tsx +++ b/packages/react-examples/src/react-experiments/Chiclet/Chiclet.Xsmall.Footer.Example.tsx @@ -51,7 +51,7 @@ export const ChicletXsmallFooterExample: React.FunctionComponent<{}> = () => { }>{ITEMS[3].activity}} foreground={ Date: Sun, 23 Nov 2025 15:28:44 -0800 Subject: [PATCH 17/69] verified new file-type-icons-preview package is rendering icons in the storybook as expected --- .../bundle-size/FileTypeIcon.fixture.js | 2 +- .../etc/file-type-icons-preview.api.md | 16 +++++ .../library/package.json | 2 + .../FileTypeIcon/FileTypeIcon.types.ts | 2 +- .../FileTypeIcon/useFileTypeIcon.ts | 35 ++++++---- .../library/src/index.ts | 3 + .../src/utils/getFileTypeIconAsHTMLString.ts | 35 ++++++++++ .../library/src/utils/getFileTypeIconAsUrl.ts | 32 +++++++++ .../src/utils/initializeFileTypeIcons.tsx | 65 +++++++++++++++++++ 9 files changed, 178 insertions(+), 14 deletions(-) create mode 100644 packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsHTMLString.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts create mode 100644 packages/react-components/file-type-icons-preview/library/src/utils/initializeFileTypeIcons.tsx diff --git a/packages/react-components/file-type-icons-preview/library/bundle-size/FileTypeIcon.fixture.js b/packages/react-components/file-type-icons-preview/library/bundle-size/FileTypeIcon.fixture.js index 762816526dc35e..a22fdd167bf53c 100644 --- a/packages/react-components/file-type-icons-preview/library/bundle-size/FileTypeIcon.fixture.js +++ b/packages/react-components/file-type-icons-preview/library/bundle-size/FileTypeIcon.fixture.js @@ -1,4 +1,4 @@ -import { FileTypeIcon } from './FileTypeIcon'; +import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; console.log(FileTypeIcon); diff --git a/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md b/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md index 777b08a66f0f02..1b3c1b7301f54a 100644 --- a/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md +++ b/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md @@ -7,11 +7,15 @@ import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import type { IIconOptions } from '@fluentui/style-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; import * as React_2 from 'react'; import type { Slot } from '@fluentui/react-utilities'; import type { SlotClassNames } from '@fluentui/react-utilities'; +// @public (undocumented) +export const DEFAULT_BASE_URL = "https://res.cdn.office.net/files/fabric-cdn-prod_20251107.003/assets/item-types/"; + // @public (undocumented) export const DEFAULT_ICON_SIZE: FileTypeIconSize; @@ -101,6 +105,12 @@ export type FileTypeIconState = ComponentState & Required): void; + // @public export const renderFileTypeIcon_unstable: (state: FileTypeIconState) => JSXElement; diff --git a/packages/react-components/file-type-icons-preview/library/package.json b/packages/react-components/file-type-icons-preview/library/package.json index 6307ead298c4fe..53252c869c1a5c 100644 --- a/packages/react-components/file-type-icons-preview/library/package.json +++ b/packages/react-components/file-type-icons-preview/library/package.json @@ -29,6 +29,8 @@ "@fluentui/react-shared-contexts": "^9.26.0", "@fluentui/react-theme": "^9.2.0", "@fluentui/react-utilities": "^9.25.4", + "@fluentui/style-utilities": "^8.13.4", + "@fluentui/utilities": "^8.15.1", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts index 241e6d0ba9d4da..ea78ccb2035986 100644 --- a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts @@ -38,7 +38,7 @@ export type FileTypeIconProps = ComponentProps & { /** * The base URL for the icon assets. If not provided, uses the default Fluent CDN. - * @default 'https://res-1.cdn.office.net/files/fabric-cdn-prod_20251119.001/assets/item-types/' + * @default 'https://res.cdn.office.net/files/fabric-cdn-prod_20251119.001/assets/item-types/' */ baseUrl?: string; }; diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts index 560b6c41eac1f4..a72e21a594e51b 100644 --- a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts @@ -1,9 +1,13 @@ import * as React from 'react'; import { getIntrinsicElementProps, slot } from '@fluentui/react-utilities'; import type { FileTypeIconProps, FileTypeIconState } from './FileTypeIcon.types'; -import { getFileTypeIconProps, DEFAULT_ICON_SIZE } from '../../utils/getFileTypeIconProps'; +import { + getFileTypeIconNameFromExtensionOrType, + getFileTypeIconSuffix, + DEFAULT_ICON_SIZE, +} from '../../utils/getFileTypeIconProps'; -const DEFAULT_BASE_URL = 'https://res-1.cdn.office.net/files/fabric-cdn-prod_20251119.001/assets/item-types/'; +const DEFAULT_BASE_URL = 'https://res.cdn.office.net/files/fabric-cdn-prod_20251119.001/assets/item-types/'; /** * Returns the props and state required to render the FileTypeIcon component. @@ -20,16 +24,23 @@ export const useFileTypeIcon_unstable = ( baseUrl = DEFAULT_BASE_URL, } = props; - // Get the icon name using the utility function - const iconProps = getFileTypeIconProps({ - extension, - type, - size, - imageFileType, - }); + // Get the base icon name and suffix separately using v8 pattern + const baseIconName = getFileTypeIconNameFromExtensionOrType(extension, type); + const baseSuffix = getFileTypeIconSuffix(size, imageFileType); + const suffixArray = baseSuffix.split('_'); // eg: ['96', '3x', 'svg'] or ['96', 'svg'] - // Construct the full icon URL - const iconUrl = `${baseUrl}${iconProps.iconName}.${imageFileType}`; + // Construct the full icon URL using v8's folder-based pattern + let iconUrl: string; + if (suffixArray.length === 3) { + // suffix is of type 96_3x_svg - it has a pixel ratio > 1 + iconUrl = `${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 + iconUrl = `${baseUrl}${size}/${baseIconName}.${suffixArray[1]}`; + } else { + // Fallback to 1x format for unexpected cases + iconUrl = `${baseUrl}${size}/${baseIconName}.${imageFileType}`; + } const state: FileTypeIconState = { size, @@ -42,7 +53,7 @@ export const useFileTypeIcon_unstable = ( getIntrinsicElementProps('img', { ref, src: iconUrl, - alt: iconProps['aria-label'] || 'File type icon', + alt: extension || 'File type icon', ...props, // Remove our custom props from being passed to the img element extension: undefined, diff --git a/packages/react-components/file-type-icons-preview/library/src/index.ts b/packages/react-components/file-type-icons-preview/library/src/index.ts index 591cfbed9e30c6..92f84fdce98496 100644 --- a/packages/react-components/file-type-icons-preview/library/src/index.ts +++ b/packages/react-components/file-type-icons-preview/library/src/index.ts @@ -23,3 +23,6 @@ export type { ImageFileType, IFileTypeIconOptions, } from './utils/getFileTypeIconProps'; +export { getFileTypeIconAsUrl } from './utils/getFileTypeIconAsUrl'; +export { getFileTypeIconAsHTMLString } from './utils/getFileTypeIconAsHTMLString'; +export { initializeFileTypeIcons, DEFAULT_BASE_URL, ICON_SIZES } from './utils/initializeFileTypeIcons'; diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsHTMLString.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsHTMLString.ts new file mode 100644 index 00000000000000..ce9ddd23ac081c --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsHTMLString.ts @@ -0,0 +1,35 @@ +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 DOM element for the `FileTypeIcon` + * as an HTML string. Similar to `getFileTypeIconProps`, this also accepts the same type of object + * but rather than returning the `iconName`, this returns the entire DOM element as a string. + * @param options + * @param baseUrl - optionally provide a custom CDN base url to fetch icons from + */ +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 baseSuffix = getFileTypeIconSuffix(size, imageFileType); // eg: 96_3x_svg or 96_png + const suffixArray = baseSuffix.split('_'); // eg: ['96', '3x', 'svg'] + + let src: string | undefined; + 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-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts new file mode 100644 index 00000000000000..73b7ae28ca07a9 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/utils/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 CDN-based URL for `FileTypeIcon`. + * Similar to `getFileTypeIconProps`, this also accepts the same type of object + * but rather than returning the `iconName`, this returns the raw URL. + * @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-components/file-type-icons-preview/library/src/utils/initializeFileTypeIcons.tsx b/packages/react-components/file-type-icons-preview/library/src/utils/initializeFileTypeIcons.tsx new file mode 100644 index 00000000000000..cfee9890512ae1 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/utils/initializeFileTypeIcons.tsx @@ -0,0 +1,65 @@ +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 { JSXElement } from '@fluentui/utilities'; + +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 function initializeFileTypeIcons(baseUrl: string = DEFAULT_BASE_URL, options?: Partial): void { + ICON_SIZES.forEach((size: number) => { + _initializeIcons(baseUrl, size, options); + }); +} + +function _initializeIcons(baseUrl: string, size: number, options?: Partial): void { + const iconTypes: string[] = Object.keys(FileTypeIconMap); + const fileTypeIcons: { [key: string]: JSXElement } = {}; + + iconTypes.forEach((type: string) => { + const baseUrlSizeType = baseUrl + size + '/' + type; + fileTypeIcons[type + size + PNG_SUFFIX] = ; + fileTypeIcons[type + size + SVG_SUFFIX] = ; + + // For high resolution screens, register additional versions + // Apply height=100% and width=100% to force image to fit into containing element + + // SVGs scale well, so you can generally use the default image. + // 1.5x is a special case where both SVGs and PNGs need a different image. + + fileTypeIcons[type + size + '_1.5x' + PNG_SUFFIX] = ( + + ); + fileTypeIcons[type + size + '_1.5x' + SVG_SUFFIX] = ( + + ); + + fileTypeIcons[type + size + '_2x' + PNG_SUFFIX] = ( + + ); + fileTypeIcons[type + size + '_3x' + PNG_SUFFIX] = ( + + ); + fileTypeIcons[type + size + '_4x' + PNG_SUFFIX] = ( + + ); + }); + + registerIcons( + { + fontFace: {}, + style: { + width: size, + height: size, + overflow: 'hidden', + }, + icons: fileTypeIcons, + mergeImageProps: true, + }, + options, + ); +} From 641eb02e46864a87693b87b6c2d48adda12d02f8 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Sun, 23 Nov 2025 15:47:41 -0800 Subject: [PATCH 18/69] simplified documentation. --- .../library/MIGRATION.md | 201 +++++------------- .../file-type-icons-preview/library/README.md | 93 ++------ .../library/docs/Spec.md | 63 ------ 3 files changed, 71 insertions(+), 286 deletions(-) delete mode 100644 packages/react-components/file-type-icons-preview/library/docs/Spec.md diff --git a/packages/react-components/file-type-icons-preview/library/MIGRATION.md b/packages/react-components/file-type-icons-preview/library/MIGRATION.md index c99d72f0caa5ce..b8caab721e2829 100644 --- a/packages/react-components/file-type-icons-preview/library/MIGRATION.md +++ b/packages/react-components/file-type-icons-preview/library/MIGRATION.md @@ -4,32 +4,42 @@ This guide helps you migrate from `@fluentui/react-file-type-icons` (v8) to `@fl ## Overview -The v9 version provides a React component-based API instead of utility functions, following Fluent UI v9 patterns and conventions. +The v9 version provides a **modern React component-based API** while maintaining **full backward compatibility** with all v8 utility functions. -## Package Installation +**Key Points:** + +- ✅ All v8 utilities exported and work identically +- ✅ No breaking API changes for utility functions +- 🆕 New component API available (recommended but optional) + +## Installation ```bash -# Remove the v8 package npm uninstall @fluentui/react-file-type-icons - -# Install the v9 preview package npm install @fluentui/react-file-type-icons-preview ``` -## Breaking Changes +## What's Changed + +### Package Name (Breaking Change) -### 1. Component-based API instead of utility functions +The only breaking change is the package name: -**v8 (utility-based):** +- **Old**: `@fluentui/react-file-type-icons` +- **New**: `@fluentui/react-file-type-icons-preview` + +### New Component API (Recommended) + +**v8 (utility-based) - Still works:** ```tsx -import { getFileTypeIconProps, FileIconType } from '@fluentui/react-file-type-icons'; +import { getFileTypeIconProps } from '@fluentui/react-file-type-icons'; const iconProps = getFileTypeIconProps({ extension: 'docx', size: 48 }); Document; ``` -**v9 (component-based):** +**v9 (component-based) - Recommended:** ```tsx import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; @@ -37,68 +47,11 @@ import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; ; ``` -### 2. Simplified Props - -The v9 component automatically handles icon name resolution and URL construction internally. - -**v8:** - -```tsx -const iconProps = getFileTypeIconProps({ - extension: 'docx', - size: 48, - imageFileType: 'png', -}); -// Manually construct URL and set on img element -``` - -**v9:** - -```tsx - -``` - -### 3. Type Usage - -**v8:** - -```tsx -import { FileIconType, getFileTypeIconProps } from '@fluentui/react-file-type-icons'; - -const iconProps = getFileTypeIconProps({ type: FileIconType.folder }); -``` - -**v9:** - -```tsx -import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-preview'; - -; -``` - -### 4. Custom Base URL - -**v8:** - -```tsx -const iconProps = getFileTypeIconProps({ - extension: 'docx', - size: 48, -}); -const customUrl = `https://my-cdn.com/${iconProps.iconName}`; -``` - -**v9:** - -```tsx - -``` - ## Migration Examples -### Example 1: Document Icon +### Document Icons -**Before (v8):** +**Before:** ```tsx import { getFileTypeIconProps } from '@fluentui/react-file-type-icons'; @@ -116,7 +69,7 @@ function DocumentItem({ filename }) { } ``` -**After (v9):** +**After:** ```tsx import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; @@ -133,22 +86,18 @@ function DocumentItem({ filename }) { } ``` -### Example 2: Folder Icon +### Folder Icons -**Before (v8):** +**Before:** ```tsx import { getFileTypeIconProps, FileIconType } from '@fluentui/react-file-type-icons'; -const folderIconProps = getFileTypeIconProps({ - type: FileIconType.folder, - size: 48, -}); - -Folder; +const iconProps = getFileTypeIconProps({ type: FileIconType.folder, size: 48 }); +Folder; ``` -**After (v9):** +**After:** ```tsx import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-preview'; @@ -156,93 +105,39 @@ import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-prev ; ``` -### Example 3: Dynamic File List +## Incremental Migration -**Before (v8):** +**You don't have to migrate all code at once!** All v8 utilities work identically in v9: ```tsx -import { getFileTypeIconProps } from '@fluentui/react-file-type-icons'; +import { + FileTypeIcon, // v9 component (new) + getFileTypeIconAsUrl, // v8 utility (still works) +} from '@fluentui/react-file-type-icons-preview'; -function FileList({ files }) { +function MyComponent() { return ( -
    - {files.map(file => { - const extension = file.name.split('.').pop(); - const iconProps = getFileTypeIconProps({ extension, size: 24 }); - - return ( -
  • - {file.name} - {file.name} -
  • - ); - })} -
- ); -} -``` - -**After (v9):** - -```tsx -import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; +
+ {/* Old code - still works */} + Document -function FileList({ files }) { - return ( -
    - {files.map(file => { - const extension = file.name.split('.').pop(); - - return ( -
  • - - {file.name} -
  • - ); - })} -
+ {/* New code - recommended */} + +
); } ``` -## Utilities Still Available - -If you need to access the underlying utilities for custom use cases, they are still exported: - -```tsx -import { - getFileTypeIconProps, - FileIconType, - getFileTypeIconNameFromExtensionOrType, -} from '@fluentui/react-file-type-icons-preview'; - -// Use utilities if you need custom logic -const iconName = getFileTypeIconNameFromExtensionOrType({ extension: 'docx' }); -``` - -## Benefits of Migration - -1. **Simpler API**: Use a React component instead of manually handling URLs and img elements -2. **Better TypeScript**: Full type safety with v9's improved type definitions -3. **Consistent with Fluent UI v9**: Follows the same patterns as other v9 components -4. **Automatic optimization**: The component handles pixel ratio and image format internally -5. **Better accessibility**: Built-in alt text and ARIA support - -## Feature Parity - -The v9 package maintains full feature parity with v8: +## Benefits of the New Component API -- ✅ All file type icons (100+ extensions) -- ✅ Special types (folder, genericFile, listItem, etc.) -- ✅ All sizes (16, 20, 24, 32, 40, 48, 64, 96) -- ✅ SVG and PNG support -- ✅ Custom base URL -- ✅ Device pixel ratio handling +1. **Simpler** - No manual URL handling or img element construction +2. **Better TypeScript** - Full type safety with v9's type definitions +3. **Accessible** - Built-in alt text and ARIA support +4. **Optimized** - Automatic pixel ratio and format handling ## Need Help? -If you encounter issues during migration: +If you encounter issues: 1. Check the [Storybook examples](https://aka.ms/fluentui-storybook) -2. Review the [API documentation](https://aka.ms/fluentui-api) -3. Open an issue on [GitHub](https://github.com/microsoft/fluentui) +2. Open an issue on [GitHub](https://github.com/microsoft/fluentui) diff --git a/packages/react-components/file-type-icons-preview/library/README.md b/packages/react-components/file-type-icons-preview/library/README.md index 81c371e34e730b..bad0f3b4d9a11b 100644 --- a/packages/react-components/file-type-icons-preview/library/README.md +++ b/packages/react-components/file-type-icons-preview/library/README.md @@ -8,144 +8,97 @@ These are not production-ready components and **should never be used in product* ### FileTypeIcon -The `FileTypeIcon` component displays an icon representing a file type based on its extension or a special type (like folder). It automatically selects the appropriate icon from the Fluent Design file type icon set. +Displays an icon representing a file type based on its extension or a special type (like folder). Automatically selects the appropriate icon from the Fluent Design file type icon set. #### Features - 🎨 **100+ file type icons** - Supports all common file extensions - 📏 **Multiple sizes** - 16, 20, 24, 32, 40, 48, 64, and 96 pixels -- 🖼️ **Format support** - Renders icons as SVG or PNG +- 🖼️ **Format support** - SVG (default) or PNG - 📁 **Special types** - Folder, list items, and other non-file icons -- ♿ **Accessible** - Includes proper alt text for screen readers -- 🎯 **Automatic optimization** - Handles device pixel ratio for crisp display +- ♿ **Accessible** - Proper alt text for screen readers ## Usage -### Basic Usage +### Basic Examples ```tsx -import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; +import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-preview'; -// Display a Word document icon +// File extension -// Display a folder icon +// Special type -``` - -### File Extensions - -The component recognizes 100+ file extensions: - -```tsx -import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; - // Word document - // Excel spreadsheet - // PowerPoint presentation - // PDF document - // Archive file - // Video file - // Image file +// Common file types + + + ``` ### Sizes -All standard Fluent UI icon sizes are supported: - ```tsx - - - - -``` - -### Special Types - -Use the `type` prop for non-extension-based icons: - -```tsx -import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-preview'; - - - - - - ``` ### Image Format -Choose between SVG (default) or PNG: - ```tsx -// SVG (default, recommended for most cases) +// SVG (default, recommended) -// PNG (better for certain legacy scenarios) +// PNG (for legacy scenarios) ``` ### Custom CDN -Override the default Fluent CDN with your own: - ```tsx ``` -### With Custom Styling - -Apply custom className or style: +### Custom Styling ```tsx - + ``` ## Migration from v8 If you're migrating from `@fluentui/react-file-type-icons` (v8), see the [Migration Guide](./MIGRATION.md). -### Quick Comparison - -**v8 (utility-based):** +**v8:** ```tsx import { getFileTypeIconProps } from '@fluentui/react-file-type-icons'; - const iconProps = getFileTypeIconProps({ extension: 'docx', size: 48 }); Document; ``` -**v9 (component-based):** +**v9:** ```tsx import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; - ; ``` ## Utilities -The underlying utilities are also exported for advanced use cases: +Underlying utilities are exported for advanced use cases: ```tsx -import { - getFileTypeIconProps, - FileIconType, - getFileTypeIconNameFromExtensionOrType, - FileTypeIconMap, -} from '@fluentui/react-file-type-icons-preview'; +import { getFileTypeIconProps, getFileTypeIconAsUrl, FileIconType } from '@fluentui/react-file-type-icons-preview'; -// Get icon name for custom logic -const iconName = getFileTypeIconNameFromExtensionOrType({ extension: 'docx' }); +// Get icon URL +const url = getFileTypeIconAsUrl({ extension: 'docx', size: 48 }); -// Get full icon props -const iconProps = getFileTypeIconProps({ extension: 'docx', size: 48 }); +// Get icon props +const props = getFileTypeIconProps({ extension: 'docx', size: 48 }); ``` ## API diff --git a/packages/react-components/file-type-icons-preview/library/docs/Spec.md b/packages/react-components/file-type-icons-preview/library/docs/Spec.md deleted file mode 100644 index c25b450f42e8c7..00000000000000 --- a/packages/react-components/file-type-icons-preview/library/docs/Spec.md +++ /dev/null @@ -1,63 +0,0 @@ -# @fluentui/file-type-icons-preview Spec - -## Background - -_Description and use cases of this component_ - -## Prior Art - -_Include background research done for this component_ - -- _Link to Open UI research_ -- _Link to comparison of v7 and v0_ -- _Link to GitHub epic issue for the converged component_ - -## Sample Code - -_Provide some representative example code that uses the proposed API for the component_ - -## Variants - -_Describe visual or functional variants of this control, if applicable. For example, a slider could have a 2D variant._ - -## API - -_List the **Props** and **Slots** proposed for the component. Ideally this would just be a link to the component's `.types.ts` file_ - -## Structure - -- _**Public**_ -- _**Internal**_ -- _**DOM** - how the component will be rendered as HTML elements_ - -## Migration - -_Describe what will need to be done to upgrade from the existing implementations:_ - -- _Migration from v8_ -- _Migration from v0_ - -## Behaviors - -_Explain how the component will behave in use, including:_ - -- _Component States_ -- _Interaction_ - - _Keyboard_ - - _Cursor_ - - _Touch_ - - _Screen readers_ - -## Accessibility - -Base accessibility information is included in the design document. After the spec is filled and review, outcomes from it need to be communicated to design and incorporated in the design document. - -- Decide whether to use **native element** or folow **ARIA** and provide reasons -- Identify the **[ARIA](https://www.w3.org/TR/wai-aria-practices-1.2/) pattern** and, if the component is listed there, follow its specification as possible. -- Identify accessibility **variants**, the `role` ([ARIA roles](https://www.w3.org/TR/wai-aria-1.1/#role_definitions)) of the component, its `slots` and `aria-*` props. -- Describe the **keyboard navigation**: Tab Oder and Arrow Key Navigation. Describe any other keyboard **shortcuts** used -- Specify texts for **state change announcements** - [ARIA live regions - ](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) (number of available items in dropdown, error messages, confirmations, ...) -- Identify UI parts that appear on **hover or focus** and specify keyboard and screen reader interaction with them -- List cases when **focus** needs to be **trapped** in sections of the UI (for dialogs and popups or for hierarchical navigation) -- List cases when **focus** needs to be **moved programatically** (if parts of the UI are appearing/disappearing or other cases) From 59298fa73c4f43742c48f207b6d39371ca49f919 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Sun, 23 Nov 2025 21:17:19 -0800 Subject: [PATCH 19/69] simplifying getFileTypeIconAsURL and getFileTypeIconAsHTMLString --- .../library/src/index.ts | 3 +- .../src/utils/getFileTypeIconAsHTMLString.ts | 35 --------------- .../library/src/utils/getFileTypeIconAsUrl.ts | 44 ++++++++++++++----- .../library/src/utils/getFileTypeIconProps.ts | 13 +++--- 4 files changed, 40 insertions(+), 55 deletions(-) delete mode 100644 packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsHTMLString.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/index.ts b/packages/react-components/file-type-icons-preview/library/src/index.ts index 92f84fdce98496..bb3c614df484cd 100644 --- a/packages/react-components/file-type-icons-preview/library/src/index.ts +++ b/packages/react-components/file-type-icons-preview/library/src/index.ts @@ -23,6 +23,5 @@ export type { ImageFileType, IFileTypeIconOptions, } from './utils/getFileTypeIconProps'; -export { getFileTypeIconAsUrl } from './utils/getFileTypeIconAsUrl'; -export { getFileTypeIconAsHTMLString } from './utils/getFileTypeIconAsHTMLString'; +export { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from './utils/getFileTypeIconAsUrl'; export { initializeFileTypeIcons, DEFAULT_BASE_URL, ICON_SIZES } from './utils/initializeFileTypeIcons'; diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsHTMLString.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsHTMLString.ts deleted file mode 100644 index ce9ddd23ac081c..00000000000000 --- a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsHTMLString.ts +++ /dev/null @@ -1,35 +0,0 @@ -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 DOM element for the `FileTypeIcon` - * as an HTML string. Similar to `getFileTypeIconProps`, this also accepts the same type of object - * but rather than returning the `iconName`, this returns the entire DOM element as a string. - * @param options - * @param baseUrl - optionally provide a custom CDN base url to fetch icons from - */ -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 baseSuffix = getFileTypeIconSuffix(size, imageFileType); // eg: 96_3x_svg or 96_png - const suffixArray = baseSuffix.split('_'); // eg: ['96', '3x', 'svg'] - - let src: string | undefined; - 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-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts index 73b7ae28ca07a9..114de43e1f4393 100644 --- a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts +++ b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts @@ -17,16 +17,40 @@ 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'] + const { extension, size = DEFAULT_ICON_SIZE, type, imageFileType = 'svg' } = options; + const baseIconName = getFileTypeIconNameFromExtensionOrType(extension, type); + const suffix = getFileTypeIconSuffix(size, imageFileType); - 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]}`; + // suffix format: {size}_{imageType} or {size}_{pixelRatio}_{imageType} + // Examples: "16_svg", "96_3x_svg", "20_1.5x_png" + const lastUnderscoreIndex = suffix.lastIndexOf('_'); + const fileExtension = suffix.substring(lastUnderscoreIndex + 1); + const pathPrefix = suffix.substring(0, lastUnderscoreIndex); // "16" or "96_3x" or "20_1.5x" + + // CDN path format: {baseUrl}{pathPrefix}/{iconName}.{fileExtension} + // Examples: baseUrl16/docx.svg, baseUrl96_3x/docx.svg + return `${baseUrl}${pathPrefix}/${baseIconName}.${fileExtension}`; +} + +/** + * Given the `fileTypeIconOptions`, this function returns the DOM element for the `FileTypeIcon` + * as an HTML string. Similar to `getFileTypeIconProps`, this also accepts the same type of object + * but rather than returning the `iconName`, this returns the entire DOM element as a string. + * @param options + * @param baseUrl - optionally provide a custom CDN base url to fetch icons from + */ +export function getFileTypeIconAsHTMLString( + options: IFileTypeIconOptions, + baseUrl: string = DEFAULT_BASE_URL, +): string | undefined { + const url = getFileTypeIconAsUrl(options, baseUrl); + + if (!url) { + return undefined; } + + const { size = DEFAULT_ICON_SIZE, imageFileType = 'svg' } = options; + //const suffix = getFileTypeIconSuffix(size, imageFileType); + + return ``; } diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts index 3c69469e2587cf..d243f8013472e7 100644 --- a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts +++ b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts @@ -196,17 +196,14 @@ export function getFileTypeIconSuffix( let devicePixelRatioSuffix = ''; // Default is 1x // SVGs scale well, so you can generally use the default image. - // 1.5x is a special case where SVGs need a different image. - if (imageFileType === 'svg' && devicePixelRatio > 1 && devicePixelRatio <= 1.5) { - // Currently missing 1.5x SVGs at size 20, snap to 1x for now - if (size !== 20) { - devicePixelRatioSuffix = '_1.5x'; - } + if (imageFileType === 'svg' && devicePixelRatio > 1 && devicePixelRatio <= 1.9) { + // 1.5x is a special case where SVGs need a different image. + devicePixelRatioSuffix = '_1.5x'; + } else if (imageFileType === 'png') { // To look good, PNGs should use a different image for higher device pixel ratios if (devicePixelRatio > 1 && devicePixelRatio <= 1.5) { - // Currently missing 1.5x icons for size 20, snap to 2x for now - devicePixelRatioSuffix = size === 20 ? '_2x' : '_1.5x'; + devicePixelRatioSuffix ='_1.5x'; } else if (devicePixelRatio > 1.5 && devicePixelRatio <= 2) { devicePixelRatioSuffix = '_2x'; } else if (devicePixelRatio > 2 && devicePixelRatio <= 3) { From 792e79239bcca673f2949ede1bfd663eab115474 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Sun, 23 Nov 2025 21:47:27 -0800 Subject: [PATCH 20/69] updated storybook / tests to evaluate helper functions --- .../src/utils/getFileTypeIconAsUrl.test.ts | 245 ++++++++++++++++++ .../FileTypeIconUrlAndHtml.stories.tsx | 219 ++++++++++++++++ .../src/FileTypeIcon/index.stories.tsx | 1 + 3 files changed, 465 insertions(+) create mode 100644 packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.test.ts create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.test.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.test.ts new file mode 100644 index 00000000000000..7503b9b3a050ba --- /dev/null +++ b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.test.ts @@ -0,0 +1,245 @@ +import { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from './getFileTypeIconAsUrl'; +import { DEFAULT_BASE_URL } from './initializeFileTypeIcons'; + +describe('getFileTypeIconAsUrl', () => { + // Store original DPR once at the start + const originalDPR = typeof window !== 'undefined' ? window.devicePixelRatio : 1; + + beforeEach(() => { + // Reset to 1x before each test + if (typeof window !== 'undefined') { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: 1, + }); + } + }); + + afterAll(() => { + // Restore original DPR after all tests in this suite + if (typeof window !== 'undefined') { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: originalDPR, + }); + } + }); + + describe('with different DPI values', () => { + it('should return correct URL for 1x DPI with docx extension', () => { + const result = getFileTypeIconAsUrl({ extension: 'docx', size: 16, imageFileType: 'png' }, DEFAULT_BASE_URL); + expect(result).toBe(`${DEFAULT_BASE_URL}16/docx.png`); + }); + + it('should return correct URL for 1.5x DPI with pdf extension', () => { + // Mock window with 1.5x DPI + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: 1.5, + }); + + const result = getFileTypeIconAsUrl({ extension: 'pdf', size: 16, imageFileType: 'png' }, DEFAULT_BASE_URL); + expect(result).toBe(`${DEFAULT_BASE_URL}16_1.5x/pdf.png`); + }); + + it('should return correct URL for 2x DPI with xlsx extension', () => { + // Mock window with 2x DPI + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: 2, + }); + + const result = getFileTypeIconAsUrl({ extension: 'xlsx', size: 16, imageFileType: 'png' }, DEFAULT_BASE_URL); + expect(result).toBe(`${DEFAULT_BASE_URL}16_2x/xlsx.png`); + }); + }); + + describe('with SVG format', () => { + it('should return correct URL for 1x DPI with SVG', () => { + const result = getFileTypeIconAsUrl({ extension: 'docx', size: 16, imageFileType: 'svg' }, DEFAULT_BASE_URL); + expect(result).toBe(`${DEFAULT_BASE_URL}16/docx.svg`); + }); + + it('should return correct URL for 1.5x DPI with SVG', () => { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: 1.5, + }); + + const result = getFileTypeIconAsUrl({ extension: 'pdf', size: 16, imageFileType: 'svg' }, DEFAULT_BASE_URL); + expect(result).toBe(`${DEFAULT_BASE_URL}16_1.5x/pdf.svg`); + }); + + it('should return correct URL for 2x DPI with SVG (should not include DPI suffix)', () => { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: 2, + }); + + const result = getFileTypeIconAsUrl({ extension: 'xlsx', size: 16, imageFileType: 'svg' }, DEFAULT_BASE_URL); + expect(result).toBe(`${DEFAULT_BASE_URL}16/xlsx.svg`); + }); + }); + + describe('with different sizes', () => { + it('should return correct URL for size 20', () => { + const result = getFileTypeIconAsUrl({ extension: 'docx', size: 20, imageFileType: 'png' }, DEFAULT_BASE_URL); + expect(result).toBe(`${DEFAULT_BASE_URL}20/docx.png`); + }); + + it('should return correct URL for size 24', () => { + const result = getFileTypeIconAsUrl({ extension: 'pdf', size: 24, imageFileType: 'svg' }, DEFAULT_BASE_URL); + expect(result).toBe(`${DEFAULT_BASE_URL}24/pdf.svg`); + }); + + it('should return correct URL for size 48', () => { + const result = getFileTypeIconAsUrl({ extension: 'xlsx', size: 48, imageFileType: 'png' }, DEFAULT_BASE_URL); + expect(result).toBe(`${DEFAULT_BASE_URL}48/xlsx.png`); + }); + }); + + describe('with custom base URL', () => { + const customBaseUrl = 'https://custom.cdn.com/icons/'; + + it('should use custom base URL', () => { + const result = getFileTypeIconAsUrl({ extension: 'docx', size: 16, imageFileType: 'svg' }, customBaseUrl); + expect(result).toBe(`${customBaseUrl}16/docx.svg`); + }); + }); +}); + +describe('getFileTypeIconAsHTMLString', () => { + // Store original DPR once at the start + const originalDPR = typeof window !== 'undefined' ? window.devicePixelRatio : 1; + + beforeEach(() => { + // Reset to 1x before each test + if (typeof window !== 'undefined') { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: 1, + }); + } + }); + + afterAll(() => { + // Restore original DPR after all tests in this suite + if (typeof window !== 'undefined') { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: originalDPR, + }); + } + }); + + describe('with different DPI values', () => { + it('should return correct HTML string for 1x DPI with docx extension', () => { + const result = getFileTypeIconAsHTMLString( + { extension: 'docx', size: 16, imageFileType: 'png' }, + DEFAULT_BASE_URL, + ); + expect(result).toBe(``); + }); + + it('should return correct HTML string for 1.5x DPI with pdf extension', () => { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: 1.5, + }); + + const result = getFileTypeIconAsHTMLString( + { extension: 'pdf', size: 16, imageFileType: 'png' }, + DEFAULT_BASE_URL, + ); + expect(result).toBe(``); + }); + + it('should return correct HTML string for 2x DPI with xlsx extension', () => { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: 2, + }); + + const result = getFileTypeIconAsHTMLString( + { extension: 'xlsx', size: 16, imageFileType: 'png' }, + DEFAULT_BASE_URL, + ); + expect(result).toBe(``); + }); + }); + + describe('with SVG format', () => { + it('should return correct HTML string for 1x DPI with SVG', () => { + const result = getFileTypeIconAsHTMLString( + { extension: 'docx', size: 24, imageFileType: 'svg' }, + DEFAULT_BASE_URL, + ); + expect(result).toBe(``); + }); + + it('should return correct HTML string for 1.5x DPI with SVG', () => { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: 1.5, + }); + + const result = getFileTypeIconAsHTMLString( + { extension: 'pdf', size: 20, imageFileType: 'svg' }, + DEFAULT_BASE_URL, + ); + expect(result).toBe(``); + }); + }); + + describe('with different sizes', () => { + it('should include correct size attributes for size 32', () => { + const result = getFileTypeIconAsHTMLString( + { extension: 'docx', size: 32, imageFileType: 'png' }, + DEFAULT_BASE_URL, + ); + expect(result).toContain('height="32"'); + expect(result).toContain('width="32"'); + }); + + it('should include correct size attributes for size 48', () => { + const result = getFileTypeIconAsHTMLString( + { extension: 'xlsx', size: 48, imageFileType: 'svg' }, + DEFAULT_BASE_URL, + ); + expect(result).toContain('height="48"'); + expect(result).toContain('width="48"'); + }); + }); + + describe('with custom base URL', () => { + const customBaseUrl = 'https://custom.cdn.com/icons/'; + + it('should use custom base URL in HTML string', () => { + const result = getFileTypeIconAsHTMLString( + { extension: 'docx', size: 16, imageFileType: 'svg' }, + customBaseUrl, + ); + expect(result).toBe(``); + }); + }); + + describe('edge cases', () => { + it('should handle undefined URL gracefully', () => { + // This would only happen if getFileTypeIconAsUrl returns undefined + // which shouldn't happen in normal circumstances, but we test for robustness + const result = getFileTypeIconAsHTMLString({ extension: 'unknown', size: 16 }, DEFAULT_BASE_URL); + expect(result).toBeDefined(); + }); + }); +}); diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx new file mode 100644 index 00000000000000..2fd255c9c24979 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx @@ -0,0 +1,219 @@ +import * as React from 'react'; +import type { JSXElement } from '@fluentui/react-components'; +import { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from '@fluentui/file-type-icons-preview'; +import { makeStyles, shorthands } from '@griffel/react'; + +const useStyles = makeStyles({ + container: { + display: 'flex', + flexDirection: 'column', + gap: '24px', + }, + section: { + display: 'flex', + flexDirection: 'column', + gap: '12px', + ...shorthands.padding('16px'), + ...shorthands.border('1px', 'solid', '#e0e0e0'), + ...shorthands.borderRadius('8px'), + }, + sectionTitle: { + fontSize: '16px', + fontWeight: '600', + marginBottom: '8px', + }, + grid: { + display: 'grid', + gridTemplateColumns: 'repeat(3, 1fr)', + gap: '16px', + }, + card: { + display: 'flex', + flexDirection: 'column', + gap: '8px', + ...shorthands.padding('12px'), + backgroundColor: '#f5f5f5', + ...shorthands.borderRadius('4px'), + }, + cardTitle: { + fontSize: '14px', + fontWeight: '600', + marginBottom: '4px', + }, + iconPreview: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + ...shorthands.padding('12px'), + backgroundColor: '#ffffff', + ...shorthands.borderRadius('4px'), + minHeight: '60px', + }, + code: { + fontSize: '11px', + fontFamily: 'monospace', + backgroundColor: '#ffffff', + ...shorthands.padding('8px'), + ...shorthands.borderRadius('4px'), + overflowX: 'auto', + wordBreak: 'break-all', + }, + label: { + fontSize: '12px', + color: '#666', + fontWeight: '500', + }, +}); + +const commonFileTypes = ['docx', 'pdf', 'xlsx']; + +export const UrlAndHtmlFunctions = (): JSXElement => { + const styles = useStyles(); + + return ( +
+ {/* URL Function Demo */} +
+
getFileTypeIconAsUrl() - PNG Format with Different DPIs
+
+ {commonFileTypes.map(extension => ( +
+
.{extension}
+ {(['1x', '1.5x', '2x'] as const).map(dpi => { + const url = getFileTypeIconAsUrl({ + extension, + size: 48, + imageFileType: 'png', + }); + + // For demo purposes, we'll show what the URL would be for each DPI + // In real usage, the browser's devicePixelRatio would determine this + const dpiSuffix = dpi === '1x' ? '' : `_${dpi}`; + const demoUrl = url?.replace(/48/, `48${dpiSuffix}`); + + return ( +
+
{dpi} DPI:
+
{demoUrl}
+
+ ); + })} +
+ ))} +
+
+ + {/* SVG URL Demo */} +
+
getFileTypeIconAsUrl() - SVG Format with Different DPIs
+
+ {commonFileTypes.map(extension => ( +
+
.{extension}
+ {(['1x', '1.5x', '2x'] as const).map(dpi => { + const url = getFileTypeIconAsUrl({ + extension, + size: 48, + imageFileType: 'svg', + }); + + // SVG only uses 1.5x for specific DPI ranges, otherwise uses base + const dpiSuffix = dpi === '1.5x' ? '_1.5x' : ''; + const demoUrl = url?.replace(/48/, `48${dpiSuffix}`); + + return ( +
+
{dpi} DPI:
+
{demoUrl}
+
+ ); + })} +
+ ))} +
+
+ + {/* HTML String Demo */} +
+
getFileTypeIconAsHTMLString() - Visual Preview
+
+ {commonFileTypes.map(extension => ( +
+
.{extension}
+
+
+
+
HTML Output:
+
+ {getFileTypeIconAsHTMLString({ + extension, + size: 48, + imageFileType: 'svg', + })} +
+
+ ))} +
+
+ + {/* Comparison at Different Sizes */} +
+
getFileTypeIconAsHTMLString() - Different Sizes
+
+ {([16, 24, 48] as const).map(size => ( +
+
Size: {size}px
+ {commonFileTypes.map(extension => ( +
+
.{extension}:
+
+
+
+
+ ))} +
+ ))} +
+
+ + {/* Interactive Example */} +
+
Current Device Information
+
+ Device Pixel Ratio: {typeof window !== 'undefined' ? window.devicePixelRatio : 'N/A'} +
+
+ This affects which icon variant is loaded for PNG images. SVG images scale better but may use 1.5x variant + for better rendering. +
+
+
+ ); +}; + +UrlAndHtmlFunctions.parameters = { + docs: { + description: { + story: + 'Demonstrates the `getFileTypeIconAsUrl()` and `getFileTypeIconAsHTMLString()` utility functions with different DPI settings (1x, 1.5x, 2x) and common file types (docx, pdf, xlsx). These functions are useful when you need direct access to CDN URLs or HTML markup for file type icons.', + }, + }, +}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx index 04b09445521579..cc1dcba73b9b31 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx @@ -6,6 +6,7 @@ export { Default } from './FileTypeIconDefault.stories'; export { Sizes } from './FileTypeIconSizes.stories'; export { CommonFileTypes } from './FileTypeIconCommon.stories'; export { SpecialTypes } from './FileTypeIconSpecialTypes.stories'; +export { UrlAndHtmlFunctions } from './FileTypeIconUrlAndHtml.stories'; import descriptionMd from './FileTypeIconDescription.md'; From 3c64988dec9586ec82c8c364de5e54c666eb56d1 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Sun, 23 Nov 2025 23:01:17 -0800 Subject: [PATCH 21/69] organizing storybook place in nav --- .../file-type-icons-preview/stories/package.json | 1 + .../stories/src/FileTypeIcon/index.stories.tsx | 3 +-- packages/react-components/react-components/package.json | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/react-components/file-type-icons-preview/stories/package.json b/packages/react-components/file-type-icons-preview/stories/package.json index bc589fd28cb512..a4fbe99940dc2d 100644 --- a/packages/react-components/file-type-icons-preview/stories/package.json +++ b/packages/react-components/file-type-icons-preview/stories/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "devDependencies": { + "@fluentui/file-type-icons-preview": "*", "@fluentui/react-storybook-addon": "*", "@fluentui/react-storybook-addon-export-to-sandbox": "*", "@fluentui/scripts-storybook": "*", diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx index cc1dcba73b9b31..56b4dd692e1472 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; import type { Meta } from '@storybook/react'; @@ -11,7 +10,7 @@ export { UrlAndHtmlFunctions } from './FileTypeIconUrlAndHtml.stories'; import descriptionMd from './FileTypeIconDescription.md'; export default { - title: 'Preview Components/FileTypeIcon', + title: 'Icons/Filetype Icons', component: FileTypeIcon, parameters: { docs: { diff --git a/packages/react-components/react-components/package.json b/packages/react-components/react-components/package.json index 60effe6d4a3fdc..7af6d6e3be332e 100644 --- a/packages/react-components/react-components/package.json +++ b/packages/react-components/react-components/package.json @@ -77,7 +77,8 @@ "@fluentui/react-motion": "^9.11.4", "@fluentui/react-carousel": "^9.8.12", "@fluentui/react-color-picker": "^9.2.11", - "@fluentui/react-nav": "^9.3.14" + "@fluentui/react-nav": "^9.3.14", + "@fluentui/file-type-icons-preview": "^0.0.1" }, "peerDependencies": { "@types/react": ">=16.14.0 <20.0.0", From 8d379e6fef716d4fcb94d1f213d269f1fa9bfc76 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 12:54:29 -0800 Subject: [PATCH 22/69] update getFileTypeIconProps to use same mapping file FileTypeIconMap.ts to fetch by FileIconType instead of a separate enum --- .../library/src/utils/FileTypeIconMap.ts | 89 +++++++++++---- .../src/utils/getFileTypeIconAsUrl.test.ts | 71 ++++++++++++ .../library/src/utils/getFileTypeIconAsUrl.ts | 12 +- .../library/src/utils/getFileTypeIconProps.ts | 107 ++++-------------- 4 files changed, 162 insertions(+), 117 deletions(-) diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/FileTypeIconMap.ts b/packages/react-components/file-type-icons-preview/library/src/utils/FileTypeIconMap.ts index a324f893e1fbf3..4f39e24ab25af3 100644 --- a/packages/react-components/file-type-icons-preview/library/src/utils/FileTypeIconMap.ts +++ b/packages/react-components/file-type-icons-preview/library/src/utils/FileTypeIconMap.ts @@ -1,9 +1,11 @@ +import { FileIconType } from './FileIconType'; + /** * Enumeration of icon file names, and what extensions they map to. * Please keep items alphabetical. Items without extensions may require specific logic in the code to map. * Always use getFileTypeIconProps to get the most up-to-date icon at the right pixel density. */ -export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { +export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: FileIconType[] } } = { accdb: { extensions: ['accdb', 'mdb'], }, @@ -13,7 +15,9 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { archive: { extensions: ['7z', 'ace', 'arc', 'arj', 'dmg', 'gz', 'iso', 'lzh', 'pkg', 'rar', 'sit', 'tgz', 'tar', 'z'], }, - album: {}, + album: { + types: [FileIconType.album], + }, audio: { extensions: [ 'aif', @@ -275,9 +279,15 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { designer: { extensions: ['design'], }, - desktopfolder: {}, - docset: {}, - documentsfolder: {}, + desktopfolder: { + types: [FileIconType.desktopFolder], + }, + docset: { + types: [FileIconType.docset], + }, + documentsfolder: { + types: [FileIconType.documentsFolder], + }, docx: { extensions: ['doc', 'docm', 'docx', 'docb'], }, @@ -291,12 +301,18 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { extensions: ['application', 'appref-ms', 'apk', 'app', 'appx', 'exe', 'ipa', 'msi', 'xap'], }, favoritesfolder: {}, - folder: {}, + folder: { + types: [FileIconType.folder], + }, font: { extensions: ['ttf', 'otf', 'woff'], }, - form: {}, - genericfile: {}, + form: { + types: [FileIconType.form], + }, + genericfile: { + types: [FileIconType.genericFile], + }, html: { extensions: ['htm', 'html', 'mht', 'mhtml'], }, @@ -306,17 +322,27 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { link: { extensions: ['lnk', 'link', 'url', 'website', 'webloc'], }, - linkedfolder: {}, - listform: {}, - listitem: {}, + linkedfolder: { + types: [FileIconType.linkedFolder], + }, + listform: { + types: [FileIconType.listForm], + }, + listitem: { + types: [FileIconType.listItem], + }, loop: { extensions: ['fluid', 'loop', 'note'], }, - loopworkspace: {}, + loopworkspace: { + types: [FileIconType.loopworkspace], + }, officescript: { extensions: ['osts'], }, - splist: {}, + splist: { + types: [FileIconType.list], + }, mcworld: { extensions: ['mcworld'], }, @@ -359,7 +385,9 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { mpt: { extensions: ['mpt'], }, - multiple: {}, + multiple: { + types: [FileIconType.multiple], + }, one: { // This is a partial OneNote page or section export. Not whole notebooks, see "onetoc" extensions: ['one', 'onepart'], @@ -419,9 +447,15 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { ], }, photo360: {}, - picturesfolder: {}, - planner: {}, - portfolio: {}, + picturesfolder: { + types: [FileIconType.picturesFolder], + }, + planner: { + types: [FileIconType.planner], + }, + portfolio: { + types: [FileIconType.portfolio], + }, potx: { extensions: ['pot', 'potm', 'potx'], }, @@ -443,16 +477,24 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { spo: { extensions: ['aspx'], }, - sponews: {}, + sponews: { + types: [FileIconType.news], + }, spreadsheet: { extensions: ['odc', 'ods', 'gsheet', 'numbers', 'tsv'], }, rtf: { extensions: ['epub', 'gdoc', 'odt', 'rtf', 'wri', 'pages'], }, - sharedfolder: {}, - playlist: {}, - sway: {}, + sharedfolder: { + types: [FileIconType.sharedFolder], + }, + playlist: { + types: [FileIconType.playlist], + }, + sway: { + types: [FileIconType.sway], + }, sysfile: { extensions: [ 'bak', @@ -486,7 +528,9 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { 'xll', ], }, - todoitem: {}, + todoitem: { + types: [FileIconType.todoItem], + }, txt: { extensions: ['dif', 'diff', 'readme', 'out', 'plist', 'properties', 'text', 'txt'], }, @@ -519,6 +563,7 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { ], }, video: { + types: [FileIconType.stream], extensions: [ '3g2', '3gp', diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.test.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.test.ts index 7503b9b3a050ba..b543a33f118167 100644 --- a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.test.ts +++ b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.test.ts @@ -1,5 +1,76 @@ import { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from './getFileTypeIconAsUrl'; import { DEFAULT_BASE_URL } from './initializeFileTypeIcons'; +import { getFileTypeIconNameFromExtensionOrType } from './getFileTypeIconProps'; +import { FileIconType } from './FileIconType'; + +describe('getFileTypeIconNameFromExtensionOrType', () => { + describe('with FileIconType enum values', () => { + it('should map all FileIconType enum values to specific icon names', () => { + // Test all 22 enum values + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.docset)).toBe('docset'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.folder)).toBe('folder'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.genericFile)).toBe('genericfile'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.listItem)).toBe('listitem'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.sharedFolder)).toBe('sharedfolder'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.multiple)).toBe('multiple'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.stream)).toBe('video'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.news)).toBe('sponews'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.desktopFolder)).toBe('desktopfolder'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.documentsFolder)).toBe('documentsfolder'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.picturesFolder)).toBe('picturesfolder'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.linkedFolder)).toBe('linkedfolder'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.list)).toBe('splist'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.form)).toBe('form'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.sway)).toBe('sway'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.playlist)).toBe('playlist'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.loopworkspace)).toBe('loopworkspace'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.planner)).toBe('planner'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.todoItem)).toBe('todoitem'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.portfolio)).toBe('portfolio'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.album)).toBe('album'); + expect(getFileTypeIconNameFromExtensionOrType(undefined, FileIconType.listForm)).toBe('listform'); + }); + + it('should not return genericfile for any valid FileIconType enum', () => { + // Verify no enum value falls back to the default + const enumValues = [ + FileIconType.docset, + FileIconType.folder, + FileIconType.genericFile, + FileIconType.listItem, + FileIconType.sharedFolder, + FileIconType.multiple, + FileIconType.stream, + FileIconType.news, + FileIconType.desktopFolder, + FileIconType.documentsFolder, + FileIconType.picturesFolder, + FileIconType.linkedFolder, + FileIconType.list, + FileIconType.form, + FileIconType.sway, + FileIconType.playlist, + FileIconType.loopworkspace, + FileIconType.planner, + FileIconType.todoItem, + FileIconType.portfolio, + FileIconType.album, + FileIconType.listForm, + ]; + + enumValues.forEach(enumValue => { + const result = getFileTypeIconNameFromExtensionOrType(undefined, enumValue); + expect(result).toBeDefined(); + // Only FileIconType.genericFile should map to 'genericfile' + if (enumValue !== FileIconType.genericFile) { + expect(result).not.toBe('genericfile'); + } else { + expect(result).toBe('genericfile'); + } + }); + }); + }); +}); describe('getFileTypeIconAsUrl', () => { // Store original DPR once at the start diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts index 114de43e1f4393..64d3580c1df8df 100644 --- a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts +++ b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts @@ -8,8 +8,7 @@ import type { IFileTypeIconOptions } from './getFileTypeIconProps'; /** * Given the `fileTypeIconOptions`, this function returns the CDN-based URL for `FileTypeIcon`. - * Similar to `getFileTypeIconProps`, this also accepts the same type of object - * but rather than returning the `iconName`, this returns the raw URL. + * Similar to `getFileTypeIconProps`, but rather than returning the `iconName`, this returns the raw URL. * @param options * @param baseUrl - optionally provide a custom CDN base url to fetch icons from */ @@ -33,9 +32,8 @@ export function getFileTypeIconAsUrl( } /** - * Given the `fileTypeIconOptions`, this function returns the DOM element for the `FileTypeIcon` - * as an HTML string. Similar to `getFileTypeIconProps`, this also accepts the same type of object - * but rather than returning the `iconName`, this returns the entire DOM element as a string. + * Given the `fileTypeIconOptions`, similar to `getFileTypeIconProps`, this function returns + * an tag DOM element that renders the icon, as a string. * @param options * @param baseUrl - optionally provide a custom CDN base url to fetch icons from */ @@ -49,8 +47,6 @@ export function getFileTypeIconAsHTMLString( return undefined; } - const { size = DEFAULT_ICON_SIZE, imageFileType = 'svg' } = options; - //const suffix = getFileTypeIconSuffix(size, imageFileType); - + const { size = DEFAULT_ICON_SIZE } = options; return ``; } diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts index d243f8013472e7..7ffed219a658b4 100644 --- a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts +++ b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts @@ -3,29 +3,9 @@ import { FileIconType } from './FileIconType'; import type { FileIconTypeInput } from './FileIconType'; let _extensionToIconName: { [key: string]: string }; +let _typeToIconName: { [key: number]: string }; const GENERIC_FILE = 'genericfile'; -const FOLDER = 'folder'; -const SHARED_FOLDER = 'sharedfolder'; -const DOCSET_FOLDER = 'docset'; -const LIST_ITEM = 'listitem'; -const LIST = 'splist'; -const MULTIPLE_ITEMS = 'multiple'; -const NEWS = 'sponews'; -const STREAM = 'video'; -const DESKTOP_FOLDER = 'desktopfolder'; -const DOCUMENTS_FOLDER = 'documentsfolder'; -const PICTURES_FOLDER = 'picturesfolder'; -const LINKED_FOLDER = 'linkedfolder'; -const FORM = 'form'; -const SWAY = 'sway'; -const PLAYLIST = 'playlist'; -const LOOP_WORKSPACE = 'loopworkspace'; -const TODOITEM = 'todoitem'; -const PLANNER = 'planner'; -const PORTFOLIO = 'portfolio'; -const ALBUM = 'album'; -const LIST_FORM = 'listform'; export const DEFAULT_ICON_SIZE: FileTypeIconSize = 16; export type FileTypeIconSize = 16 | 20 | 24 | 32 | 40 | 48 | 64 | 96; @@ -87,7 +67,6 @@ export function getFileTypeIconNameFromExtensionOrType( extension: string | undefined, type: FileIconType | undefined, ): string { - let iconBaseName: string | undefined; if (extension) { if (!_extensionToIconName) { _extensionToIconName = {}; @@ -108,74 +87,28 @@ export function getFileTypeIconNameFromExtensionOrType( // Strip periods, force lowercase. extension = extension.replace('.', '').toLowerCase(); return _extensionToIconName[extension] || GENERIC_FILE; + } else if (type) { - switch (type) { - case FileIconType.docset: - iconBaseName = DOCSET_FOLDER; - break; - case FileIconType.folder: - iconBaseName = FOLDER; - break; - case FileIconType.listItem: - iconBaseName = LIST_ITEM; - break; - case FileIconType.sharedFolder: - iconBaseName = SHARED_FOLDER; - break; - case FileIconType.stream: - iconBaseName = STREAM; - break; - case FileIconType.multiple: - iconBaseName = MULTIPLE_ITEMS; - break; - case FileIconType.news: - iconBaseName = NEWS; - break; - case FileIconType.desktopFolder: - iconBaseName = DESKTOP_FOLDER; - break; - case FileIconType.documentsFolder: - iconBaseName = DOCUMENTS_FOLDER; - break; - case FileIconType.picturesFolder: - iconBaseName = PICTURES_FOLDER; - break; - case FileIconType.linkedFolder: - iconBaseName = LINKED_FOLDER; - break; - case FileIconType.list: - iconBaseName = LIST; - break; - case FileIconType.form: - iconBaseName = FORM; - break; - case FileIconType.sway: - iconBaseName = SWAY; - break; - case FileIconType.playlist: - iconBaseName = PLAYLIST; - break; - case FileIconType.loopworkspace: - iconBaseName = LOOP_WORKSPACE; - break; - case FileIconType.planner: - iconBaseName = PLANNER; - break; - case FileIconType.todoItem: - iconBaseName = TODOITEM; - break; - case FileIconType.portfolio: - iconBaseName = PORTFOLIO; - break; - case FileIconType.album: - iconBaseName = ALBUM; - break; - case FileIconType.listForm: - iconBaseName = LIST_FORM; - break; + if (!_typeToIconName) { + _typeToIconName = {}; + + for (const iconName in FileTypeIconMap) { + if (FileTypeIconMap.hasOwnProperty(iconName)) { + const types = FileTypeIconMap[iconName].types; + + if (types) { + for (let i = 0; i < types.length; i++) { + _typeToIconName[types[i]] = iconName; + } + } + } + } } + + return _typeToIconName[type] || GENERIC_FILE; } - return iconBaseName || GENERIC_FILE; + + return GENERIC_FILE; } /** From e51a9c6b5896c0916bd42dfac7289ea6e0d6be92 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 12:56:30 -0800 Subject: [PATCH 23/69] new documentation pass 1 (broken) --- .../FileTypeIconAccessibility.stories.tsx | 217 ++++++++++++++ .../FileTypeIcon/FileTypeIconBestPractices.md | 52 ++++ .../FileTypeIconCommon.stories.tsx | 170 +++++++++-- .../FileTypeIcon/FileTypeIconDescription.md | 27 +- .../FileTypeIconEdgeCases.stories.tsx | 270 ++++++++++++++++++ .../FileTypeIconSizes.stories.tsx | 63 ++-- .../src/FileTypeIcon/index.stories.tsx | 40 ++- 7 files changed, 784 insertions(+), 55 deletions(-) create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconAccessibility.stories.tsx create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconAccessibility.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconAccessibility.stories.tsx new file mode 100644 index 00000000000000..68b1be69558809 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconAccessibility.stories.tsx @@ -0,0 +1,217 @@ +import * as React from 'react'; +import type { JSXElement } from '@fluentui/react-components'; +import { tokens } from '@fluentui/react-components'; +import { FileTypeIcon, FileIconType } from '@fluentui/file-type-icons-preview'; +import { makeStyles, shorthands } from '@griffel/react'; + +const useStyles = makeStyles({ + container: { + display: 'flex', + flexDirection: 'column', + gap: '32px', + }, + section: { + display: 'flex', + flexDirection: 'column', + gap: '12px', + }, + sectionTitle: { + fontSize: tokens.fontSizeBase400, + fontWeight: tokens.fontWeightSemibold, + marginBottom: '8px', + }, + description: { + fontSize: tokens.fontSizeBase300, + color: tokens.colorNeutralForeground3, + marginBottom: '8px', + }, + exampleRow: { + display: 'flex', + alignItems: 'center', + gap: '16px', + ...shorthands.padding('12px'), + backgroundColor: tokens.colorNeutralBackground1, + ...shorthands.borderRadius(tokens.borderRadiusMedium), + }, + fileInfo: { + display: 'flex', + flexDirection: 'column', + gap: '4px', + }, + fileName: { + fontSize: tokens.fontSizeBase300, + fontWeight: tokens.fontWeightSemibold, + }, + fileDetails: { + fontSize: tokens.fontSizeBase200, + color: tokens.colorNeutralForeground3, + }, + codeBlock: { + backgroundColor: tokens.colorNeutralBackground3, + ...shorthands.padding('8px', '12px'), + ...shorthands.borderRadius(tokens.borderRadiusMedium), + fontFamily: tokens.fontFamilyMonospace, + fontSize: tokens.fontSizeBase200, + overflowX: 'auto', + }, + highContrastDemo: { + '@media (forced-colors: active)': { + ...shorthands.border('2px', 'solid', 'CanvasText'), + ...shorthands.padding('16px'), + ...shorthands.borderRadius(tokens.borderRadiusMedium), + }, + }, + listExample: { + display: 'flex', + flexDirection: 'column', + gap: '8px', + }, + listItem: { + display: 'flex', + alignItems: 'center', + gap: '12px', + ...shorthands.padding('8px', '12px'), + backgroundColor: tokens.colorNeutralBackground1, + ...shorthands.borderRadius(tokens.borderRadiusMedium), + ':hover': { + backgroundColor: tokens.colorNeutralBackground1Hover, + }, + ':focus-within': { + ...shorthands.outline('2px', 'solid', tokens.colorStrokeFocus2), + outlineOffset: '2px', + }, + }, + link: { + textDecoration: 'none', + color: 'inherit', + display: 'flex', + alignItems: 'center', + gap: '12px', + flex: 1, + ':focus': { + ...shorthands.outline('none'), + }, + }, +}); + +export const Accessibility = (): JSXElement => { + const styles = useStyles(); + + return ( +
+
+
Default Alt Text
+
+ FileTypeIcon automatically generates descriptive alt text based on the file extension or type. This ensures + screen readers can convey the file type to users. +
+
+ +
+
Annual Report.pdf
+
Alt text: "pdf file icon"
+
+
+
+ +
+
Documents
+
Alt text: "folder file icon"
+
+
+ + {` +// Rendered with alt="pdf file icon"`} + +
+ +
+
Context with File Names
+
+ Always pair file type icons with file names or labels. The icon provides visual recognition while the text + ensures accessibility for all users. +
+
+
+ + Project Proposal.docx +
+
+ + Budget 2025.xlsx +
+
+ + Q4 Presentation.pptx +
+
+
+ +
+
Keyboard Navigation
+
+ When icons are part of interactive elements, ensure proper keyboard navigation and focus indicators are + present. +
+ +
+ Try pressing Tab to navigate through the list above. Focus indicators clearly show the active item. +
+
+ +
+
High Contrast Mode Support
+
+ FileTypeIcon respects Windows High Contrast mode and other forced-colors settings, ensuring icons remain + visible and distinguishable. +
+
+ + + + + +
+
+ Icons automatically adapt to high contrast themes while maintaining their recognizable shapes. +
+
+ +
+
Screen Reader Announcements
+
+ When using icons in dynamic content (like file upload feedback), ensure screen readers announce changes + appropriately using ARIA live regions. +
+ + {`
+ + Report.pdf uploaded successfully +
`} +
+
+
+ ); +}; + +Accessibility.parameters = { + docs: { + description: { + story: + 'FileTypeIcon includes built-in accessibility features like automatic alt text generation, support for high contrast mode, and compatibility with screen readers. Always provide surrounding context (file names, labels) to ensure all users can identify files effectively.', + }, + }, +}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md new file mode 100644 index 00000000000000..5c361f463f1f2a --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md @@ -0,0 +1,52 @@ +# FileTypeIcon Best Practices + +## Do + +- **Use SVG format (default) for better scalability and smaller file sizes** - SVG icons scale perfectly at any size and are more performant than PNG. +- **Choose appropriate sizes based on UI density**: + - Use 16-20px for compact interfaces, toolbars, and dense lists + - Use 24-32px for standard list items and cards + - Use 40-48px for featured content and file pickers + - Use 64-96px only for hero sections or file upload zones +- **Provide meaningful context through surrounding UI** - File type icons work best when combined with file names, metadata, or labels. +- **Use special types for non-file entities** - Use `folder`, `sharedFolder`, `listItem`, `docset`, or `genericFile` types for appropriate contexts instead of trying to force file extensions. +- **Handle unknown file extensions gracefully** - The component automatically falls back to `genericFile` for unrecognized extensions. +- **Consider accessibility** - The component provides default alt text, but ensure the surrounding context makes the file's purpose clear to all users. +- **Use consistent sizes within the same UI context** - Mixing different icon sizes in a single list or grid creates visual inconsistency. + +## Don't + +- **Don't use file type icons as the sole means of identifying files** - Always pair icons with file names or descriptions for better usability and accessibility. +- **Don't use file type icons as primary navigation elements** - These icons are meant to represent file types, not actions or navigation destinations. +- **Don't override the default alt text without good reason** - The component provides sensible defaults based on the file extension or type. +- **Don't use very large sizes (64px+) in dense lists or tables** - Large icons create excessive whitespace and reduce content density. +- **Don't mix PNG and SVG formats in the same UI context** - Stick to one format for visual consistency; prefer SVG unless you have specific compatibility requirements. +- **Don't forget to handle loading states** - When displaying many icons, consider performance implications and implement appropriate loading strategies. +- **Don't use file type icons for branding or decorative purposes** - These icons follow Fluent Design System conventions and are meant for functional file type representation. +- **Don't assume all file types have unique icons** - Many extensions share the same generic icon; rely on file names for precise identification. + +## Usage Guidelines + +### In Lists and Tables + +Use smaller sizes (16-24px) and ensure adequate spacing between icons and text labels for readability. + +### In File Upload Interfaces + +Use medium to large sizes (40-64px) to create clear drop zones and visual feedback during file selection. + +### In Search Results + +Combine icons with file metadata (size, date modified, path) to help users quickly identify files. + +### In Breadcrumbs and Navigation + +Use small sizes (16-20px) to maintain compact navigation while providing visual cues about file types. + +### With Custom Styling + +When applying custom styles, maintain the icon's aspect ratio and avoid distorting the visual design. + +### Performance Considerations + +When rendering many icons (100+), consider virtualization and lazy loading techniques to maintain smooth performance. diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx index 6c05d0b3d4bf64..2f40d2504a6d76 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx @@ -1,10 +1,26 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; +import { tokens } from '@fluentui/react-components'; import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; -import { makeStyles } from '@griffel/react'; +import { makeStyles, shorthands } from '@griffel/react'; const useStyles = makeStyles({ container: { + display: 'flex', + flexDirection: 'column', + gap: '32px', + }, + categorySection: { + display: 'flex', + flexDirection: 'column', + gap: '12px', + }, + categoryTitle: { + fontSize: tokens.fontSizeBase400, + fontWeight: tokens.fontWeightSemibold, + marginBottom: '8px', + }, + grid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))', gap: '16px', @@ -14,43 +30,144 @@ const useStyles = makeStyles({ flexDirection: 'column', alignItems: 'center', gap: '8px', + ...shorthands.padding('12px'), + backgroundColor: tokens.colorNeutralBackground1, + ...shorthands.borderRadius(tokens.borderRadiusMedium), + ':hover': { + backgroundColor: tokens.colorNeutralBackground1Hover, + }, }, label: { - fontSize: '12px', + fontSize: tokens.fontSizeBase200, textAlign: 'center', + color: tokens.colorNeutralForeground2, }, }); -const commonExtensions = [ - 'docx', - 'xlsx', - 'pptx', - 'pdf', - 'txt', - 'zip', - 'png', - 'jpg', - 'mp4', - 'mp3', - 'html', - 'css', - 'js', - 'json', - 'xml', - 'csv', -]; +const fileTypeCategories = { + documents: ['docx', 'doc', 'pdf', 'txt', 'rtf', 'odt'], + spreadsheets: ['xlsx', 'xls', 'csv', 'ods'], + presentations: ['pptx', 'ppt', 'odp'], + images: ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'svg', 'ico', 'webp'], + media: ['mp4', 'avi', 'mov', 'wmv', 'mp3', 'wav', 'flac', 'aac'], + web: ['html', 'htm', 'css', 'js', 'jsx', 'ts', 'tsx', 'json', 'xml'], + code: ['py', 'java', 'c', 'cpp', 'cs', 'go', 'rb', 'php', 'swift'], + archives: ['zip', 'rar', '7z', 'tar', 'gz', 'iso'], + data: ['sql', 'db', 'mdb', 'accdb', 'xml', 'json', 'yaml'], +}; export const CommonFileTypes = (): JSXElement => { const styles = useStyles(); return (
- {commonExtensions.map(ext => ( -
- -
.{ext}
+
+
Documents
+
+ {fileTypeCategories.documents.map(ext => ( +
+ +
.{ext}
+
+ ))} +
+
+ +
+
Spreadsheets
+
+ {fileTypeCategories.spreadsheets.map(ext => ( +
+ +
.{ext}
+
+ ))} +
+
+ +
+
Presentations
+
+ {fileTypeCategories.presentations.map(ext => ( +
+ +
.{ext}
+
+ ))} +
+
+ +
+
Images
+
+ {fileTypeCategories.images.map(ext => ( +
+ +
.{ext}
+
+ ))} +
+
+ +
+
Audio & Video
+
+ {fileTypeCategories.media.map(ext => ( +
+ +
.{ext}
+
+ ))} +
+
+ +
+
Web Files
+
+ {fileTypeCategories.web.map(ext => ( +
+ +
.{ext}
+
+ ))} +
+
+ +
+
Programming
+
+ {fileTypeCategories.code.map(ext => ( +
+ +
.{ext}
+
+ ))} +
+
+ +
+
Archives
+
+ {fileTypeCategories.archives.map(ext => ( +
+ +
.{ext}
+
+ ))} +
+
+ +
+
Data & Databases
+
+ {fileTypeCategories.data.map(ext => ( +
+ +
.{ext}
+
+ ))}
- ))} +
); }; @@ -58,7 +175,8 @@ export const CommonFileTypes = (): JSXElement => { CommonFileTypes.parameters = { docs: { description: { - story: 'FileTypeIcon displays different icons for various common file types and extensions.', + story: + 'FileTypeIcon supports a comprehensive library of common file extensions organized by category. This includes documents, spreadsheets, presentations, images, media files, web files, code files, archives, and database files. Each file type has a distinct, recognizable icon.', }, }, }; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md index c8ed40f13b3c93..8b1dd53675017e 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md @@ -1,13 +1,26 @@ # FileTypeIcon -FileTypeIcon displays an icon representing a file type based on its extension or a special type (like folder). +Filetype icons represent a file or other "digital object" based on its extension or a special type (like folder). The Fluent UI design system component helps you provide consistent, recognizable visual representations of the user's items and documents across your application aligned with Microsoft 365. -The component automatically selects the appropriate icon from the Fluent Design file type icon set and handles device pixel ratio for optimal display quality. +The component automatically selects the appropriate icon from the comprehensive Fluent Design file type icon set and handles device pixel ratio for optimal display quality on all screens. + +## When to use Filetype Icons + +Use FileTypeIcon when you need to: + +- **Display file lists or grids** - Help users quickly identify file types in recent lists, document libraries, file browsers, or search results +- **Show file attachments** - Indicate attachment types in emails, messages, or forms +- **Create file upload interfaces** - Provide visual feedback about accepted or uploaded file types +- **Represent documents in workflows** - Show file types in approval processes, cloud content management interfaces, document workflows, or collaboration tools + +This control integrates seamlessly with other Fluent UI v9 components using the same design principles. It can be used in `DataGrid`, `List`, and `Card` components for displaying file collections, following consistent theming, spacing and sizing patterns with the broader Fluent Design System. ## Features -- **Automatic icon selection**: Matches file extensions to the appropriate icon -- **Multiple sizes**: Supports 16, 20, 24, 32, 40, 48, 64, and 96 pixel sizes -- **Format support**: Renders icons as SVG or PNG -- **Special types**: Supports non-file-based icons like folders, list items, etc. -- **Accessibility**: Includes appropriate alt text for screen readers +- **Automatic icon selection**: Matches file extensions to the appropriate icon from a comprehensive library +- **Multiple sizes**: Supports 16, 20, 24, 32, 40, 48, 64, and 96 pixel sizes for different UI contexts +- **Format support**: Renders icons as SVG (default, recommended) or PNG for maximum compatibility +- **Special types**: Supports non-file-based icons like folders, shared folders, list items, docsets, and generic files +- **Accessibility**: Includes appropriate alt text for screen readers and follows WCAG guidelines +- **Device-aware rendering**: Automatically handles different pixel densities for crisp display on all screens +- **Customizable base URL**: Configure a custom CDN or asset path for icon resources diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx new file mode 100644 index 00000000000000..e2364404655111 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx @@ -0,0 +1,270 @@ +import * as React from 'react'; +import type { JSXElement } from '@fluentui/react-components'; +import { tokens } from '@fluentui/react-components'; +import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import { makeStyles, shorthands } from '@griffel/react'; + +const useStyles = makeStyles({ + container: { + display: 'flex', + flexDirection: 'column', + gap: '32px', + }, + section: { + display: 'flex', + flexDirection: 'column', + gap: '12px', + }, + sectionTitle: { + fontSize: tokens.fontSizeBase400, + fontWeight: tokens.fontWeightSemibold, + marginBottom: '8px', + }, + description: { + fontSize: tokens.fontSizeBase300, + color: tokens.colorNeutralForeground3, + marginBottom: '8px', + }, + exampleGrid: { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', + gap: '16px', + }, + exampleItem: { + display: 'flex', + alignItems: 'center', + gap: '12px', + ...shorthands.padding('12px'), + backgroundColor: tokens.colorNeutralBackground1, + ...shorthands.borderRadius(tokens.borderRadiusMedium), + }, + label: { + fontSize: tokens.fontSizeBase300, + fontFamily: tokens.fontFamilyMonospace, + }, + fallbackNote: { + fontSize: tokens.fontSizeBase200, + color: tokens.colorNeutralForeground3, + fontStyle: 'italic', + }, + warningBox: { + ...shorthands.padding('12px', '16px'), + backgroundColor: tokens.colorPaletteYellowBackground2, + ...shorthands.borderLeft('4px', 'solid', tokens.colorPaletteYellowBorder1), + ...shorthands.borderRadius(tokens.borderRadiusMedium), + fontSize: tokens.fontSizeBase300, + }, + codeBlock: { + backgroundColor: tokens.colorNeutralBackground3, + ...shorthands.padding('12px'), + ...shorthands.borderRadius(tokens.borderRadiusMedium), + fontFamily: tokens.fontFamilyMonospace, + fontSize: tokens.fontSizeBase200, + overflowX: 'auto', + }, +}); + +export const EdgeCases = (): JSXElement => { + const styles = useStyles(); + + return ( +
+
+
Unknown Extensions
+
+ When FileTypeIcon encounters an unknown or unsupported file extension, it automatically falls back to the + generic file icon, ensuring consistent visual representation. +
+
+
+ +
+
.xyz123
+
→ genericFile
+
+
+
+ +
+
.unknown
+
→ genericFile
+
+
+
+ +
+
.custom
+
→ genericFile
+
+
+
+ +
+
.proprietary
+
→ genericFile
+
+
+
+
+ +
+
Empty or Null Extensions
+
+ When no extension is provided or the extension is empty, the component gracefully handles the case by showing + the generic file icon. +
+
+
+ +
+
extension=""
+
→ genericFile
+
+
+
+ +
+
no extension prop
+
→ genericFile
+
+
+
+
+ +
+
Special Characters in Extensions
+
+ Extensions with special characters, numbers, or unusual formatting are handled gracefully, though they + typically fall back to the generic icon if not recognized. +
+
+
+ +
+
.file.bak
+
Handles dots
+
+
+
+ +
+
.tar.gz
+
Double extension
+
+
+
+ +
+
.123
+
Numeric only
+
+
+
+ +
+
.v2
+
Alphanumeric
+
+
+
+
+ +
+
Very Long File Names
+
+ The icon component focuses on the extension, not the filename length. Regardless of filename length, the + correct icon is displayed. However, UI layouts should handle text truncation appropriately. +
+
+ +
+
+ This_is_a_very_long_filename_that_could_cause_layout_issues_in_some_contexts_Q4_2025_Final_Report_v3.pdf +
+
+
+
+ Note: While FileTypeIcon handles any extension length, consider truncating long filenames in + your UI layout to maintain readability and prevent overflow issues. +
+
+ +
+
Case Sensitivity
+
+ File extensions are handled in a case-insensitive manner. The component recognizes extensions regardless of + capitalization. +
+
+
+ +
.PDF
+
+
+ +
.pdf
+
+
+ +
.Pdf
+
+
+ +
.PdF
+
+
+
All variations display the same PDF icon
+
+ +
+
Best Practice: Handle Edge Cases in Your Code
+
+ When integrating FileTypeIcon, consider extracting and normalizing file extensions from full filenames: +
+ + {`// Extract extension from filename +const getExtension = (filename: string): string => { + const parts = filename.split('.'); + return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : ''; +}; + +// Usage +const filename = "Report.Final.PDF"; +const extension = getExtension(filename); // "pdf" + +`} + +
+ +
+
Extensions with Leading Dots
+
+ The extension prop should be provided without the leading dot. If a dot is included, the component handles it + appropriately. +
+
+
+ +
extension="docx" ✓
+
+
+ +
extension=".docx"
+
+
+
+ Best Practice: Always provide extensions without the leading dot for consistent behavior. +
+
+
+ ); +}; + +EdgeCases.parameters = { + docs: { + description: { + story: + 'FileTypeIcon gracefully handles edge cases including unknown extensions, empty values, special characters, and case variations. The component falls back to a generic file icon for unrecognized extensions, ensuring your UI always displays something meaningful.', + }, + }, +}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx index 1b443b1593327c..75b3fc6d985b40 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx @@ -1,21 +1,32 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; +import { tokens } from '@fluentui/react-components'; import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; import { makeStyles } from '@griffel/react'; const useStyles = makeStyles({ container: { display: 'flex', - flexDirection: 'column', - gap: '16px', + alignItems: 'flex-end', + gap: '24px', + flexWrap: 'wrap', }, - row: { + iconGroup: { display: 'flex', + flexDirection: 'column', alignItems: 'center', gap: '8px', }, label: { - width: '100px', + fontSize: tokens.fontSizeBase200, + fontWeight: tokens.fontWeightSemibold, + color: tokens.colorNeutralForeground2, + }, + description: { + fontSize: tokens.fontSizeBase100, + color: tokens.colorNeutralForeground3, + textAlign: 'center', + maxWidth: '120px', }, }); @@ -24,37 +35,45 @@ export const Sizes = (): JSXElement => { return (
-
-
16px:
+
+
16px
+
Compact UIs, toolbars, dense tables
-
-
20px:
+
+
20px
+
Compact lists, navigation items
-
-
24px:
+
+
24px
+
Standard list items, search results
-
-
32px:
+
+
32px
+
Standard cards, file browsers
-
-
40px:
+
+
40px
+
Featured items, file pickers
-
-
48px:
+
+
48px
+
Grid views, attachment previews
-
-
64px:
+
+
64px
+
Large grid items, file upload zones
-
-
96px:
+
+
96px
+
Hero sections, large previews
); @@ -63,7 +82,9 @@ export const Sizes = (): JSXElement => { Sizes.parameters = { docs: { description: { - story: 'FileTypeIcon supports multiple sizes: 16, 20, 24, 32, 40, 48, 64, and 96 pixels.', + story: + 'FileTypeIcon supports 8 size variants (16, 20, 24, 32, 40, 48, 64, and 96 pixels) to accommodate different UI contexts. Choose smaller sizes for compact interfaces and larger sizes for featured content or file upload experiences.', }, }, }; + diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx index 56b4dd692e1472..93a907a7b972b3 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx @@ -6,8 +6,11 @@ export { Sizes } from './FileTypeIconSizes.stories'; export { CommonFileTypes } from './FileTypeIconCommon.stories'; export { SpecialTypes } from './FileTypeIconSpecialTypes.stories'; export { UrlAndHtmlFunctions } from './FileTypeIconUrlAndHtml.stories'; +export { Accessibility } from './FileTypeIconAccessibility.stories'; +export { EdgeCases } from './FileTypeIconEdgeCases.stories'; import descriptionMd from './FileTypeIconDescription.md'; +import bestPracticesMd from './FileTypeIconBestPractices.md'; export default { title: 'Icons/Filetype Icons', @@ -15,7 +18,42 @@ export default { parameters: { docs: { description: { - component: descriptionMd, + component: [descriptionMd, bestPracticesMd].join('\n'), + }, + }, + }, + argTypes: { + size: { + control: { type: 'select' }, + options: [16, 20, 24, 32, 40, 48, 64, 96], + description: 'The size of the icon in pixels', + table: { + type: { summary: '16 | 20 | 24 | 32 | 40 | 48 | 64 | 96' }, + defaultValue: { summary: '48' }, + }, + }, + extension: { + control: { type: 'text' }, + description: 'The file extension (without the dot)', + table: { + type: { summary: 'string' }, + }, + }, + type: { + control: { type: 'select' }, + options: ['folder', 'genericFile', 'sharedFolder', 'listItem', 'docset'], + description: 'Special icon type (alternative to extension)', + table: { + type: { summary: 'FileIconType' }, + }, + }, + imageFileType: { + control: { type: 'radio' }, + options: ['svg', 'png'], + description: 'The image format to use', + table: { + type: { summary: "'svg' | 'png'" }, + defaultValue: { summary: "'svg'" }, }, }, }, From 213d6f07422c22b5d10964874244cebba827ec89 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 13:24:08 -0800 Subject: [PATCH 24/69] documentation rendering ok, ready to condense --- .../FileTypeIconCommon.stories.tsx | 81 ++----------------- .../FileTypeIconDefault.stories.tsx | 6 ++ .../FileTypeIcon/FileTypeIconDescription.md | 3 +- 3 files changed, 15 insertions(+), 75 deletions(-) diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx index 2f40d2504a6d76..5616aebcdc96a3 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx @@ -45,15 +45,10 @@ const useStyles = makeStyles({ }); const fileTypeCategories = { - documents: ['docx', 'doc', 'pdf', 'txt', 'rtf', 'odt'], - spreadsheets: ['xlsx', 'xls', 'csv', 'ods'], - presentations: ['pptx', 'ppt', 'odp'], - images: ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'svg', 'ico', 'webp'], - media: ['mp4', 'avi', 'mov', 'wmv', 'mp3', 'wav', 'flac', 'aac'], - web: ['html', 'htm', 'css', 'js', 'jsx', 'ts', 'tsx', 'json', 'xml'], - code: ['py', 'java', 'c', 'cpp', 'cs', 'go', 'rb', 'php', 'swift'], - archives: ['zip', 'rar', '7z', 'tar', 'gz', 'iso'], - data: ['sql', 'db', 'mdb', 'accdb', 'xml', 'json', 'yaml'], + documents: ['docx', 'xlsx', 'pdf', 'txt', 'rtf', 'odt', 'pptx', 'csv'], + media: ['jpg', 'svg', 'mp4', 'mp3', 'wav', 'aac'], + code: ['html', 'url', 'json', 'xml', 'py', 'java', 'cpp'], + data: ['zip', 'tar', 'sql', 'accdb', 'xml'], }; export const CommonFileTypes = (): JSXElement => { @@ -62,7 +57,7 @@ export const CommonFileTypes = (): JSXElement => { return (
-
Documents
+
Common Documents
{fileTypeCategories.documents.map(ext => (
@@ -74,43 +69,7 @@ export const CommonFileTypes = (): JSXElement => {
-
Spreadsheets
-
- {fileTypeCategories.spreadsheets.map(ext => ( -
- -
.{ext}
-
- ))} -
-
- -
-
Presentations
-
- {fileTypeCategories.presentations.map(ext => ( -
- -
.{ext}
-
- ))} -
-
- -
-
Images
-
- {fileTypeCategories.images.map(ext => ( -
- -
.{ext}
-
- ))} -
-
- -
-
Audio & Video
+
Media Files
{fileTypeCategories.media.map(ext => (
@@ -122,19 +81,7 @@ export const CommonFileTypes = (): JSXElement => {
-
Web Files
-
- {fileTypeCategories.web.map(ext => ( -
- -
.{ext}
-
- ))} -
-
- -
-
Programming
+
Web & Programming
{fileTypeCategories.code.map(ext => (
@@ -146,19 +93,7 @@ export const CommonFileTypes = (): JSXElement => {
-
Archives
-
- {fileTypeCategories.archives.map(ext => ( -
- -
.{ext}
-
- ))} -
-
- -
-
Data & Databases
+
Data Files
{fileTypeCategories.data.map(ext => (
diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx index ae1ba2eed2f3c2..a31c8f09ae04d4 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx @@ -4,3 +4,9 @@ import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; import type { FileTypeIconProps } from '@fluentui/file-type-icons-preview'; export const Default = (props: Partial): JSXElement => ; + +Default.args = { + extension: 'docx', + size: 48, + imageFileType: 'svg', +}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md index 8b1dd53675017e..d5b06be38d7e8c 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md @@ -9,8 +9,7 @@ The component automatically selects the appropriate icon from the comprehensive Use FileTypeIcon when you need to: - **Display file lists or grids** - Help users quickly identify file types in recent lists, document libraries, file browsers, or search results -- **Show file attachments** - Indicate attachment types in emails, messages, or forms -- **Create file upload interfaces** - Provide visual feedback about accepted or uploaded file types +- **Show file attachments or file upload interfaces** - Indicate attachment types in emails, messages, or forms. Provide visual feedback about accepted or uploaded file types - **Represent documents in workflows** - Show file types in approval processes, cloud content management interfaces, document workflows, or collaboration tools This control integrates seamlessly with other Fluent UI v9 components using the same design principles. It can be used in `DataGrid`, `List`, and `Card` components for displaying file collections, following consistent theming, spacing and sizing patterns with the broader Fluent Design System. From f0a02bf94c9f0f128838d662015387d3cbe99dca Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 15:12:50 -0800 Subject: [PATCH 25/69] refining FileIconType and storybook --- .../FileTypeIcon/FileTypeIcon.types.ts | 4 ++-- .../library/src/index.ts | 1 - .../library/src/utils/FileIconType.ts | 24 ------------------- .../library/src/utils/getFileTypeIconProps.ts | 3 +-- .../FileTypeIconDefault.stories.tsx | 2 +- .../src/FileTypeIcon/index.stories.tsx | 3 +-- 6 files changed, 5 insertions(+), 32 deletions(-) diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts index ea78ccb2035986..01469d9cd2a8e0 100644 --- a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts @@ -1,5 +1,5 @@ import type { ComponentState, ComponentProps, Slot } from '@fluentui/react-utilities'; -import type { FileIconTypeInput } from '../../utils/FileIconType'; +import type { FileIconType } from '../../utils/FileIconType'; import type { FileTypeIconSize, ImageFileType } from '../../utils/getFileTypeIconProps'; export type FileTypeIconSlots = { @@ -22,7 +22,7 @@ export type FileTypeIconProps = ComponentProps & { * file type icons that are not associated with a file extension, * such as folder. */ - type?: FileIconTypeInput; + type?: FileIconType; /** * The size of the icon in pixels. diff --git a/packages/react-components/file-type-icons-preview/library/src/index.ts b/packages/react-components/file-type-icons-preview/library/src/index.ts index bb3c614df484cd..279136bbbb0973 100644 --- a/packages/react-components/file-type-icons-preview/library/src/index.ts +++ b/packages/react-components/file-type-icons-preview/library/src/index.ts @@ -10,7 +10,6 @@ export type { FileTypeIconProps, FileTypeIconSlots, FileTypeIconState } from './ // Utility exports (for backward compatibility and advanced usage) export { FileIconType } from './utils/FileIconType'; -export type { FileIconTypeInput } from './utils/FileIconType'; export { FileTypeIconMap } from './utils/FileTypeIconMap'; export { getFileTypeIconProps, diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/FileIconType.ts b/packages/react-components/file-type-icons-preview/library/src/utils/FileIconType.ts index 22a7ee22bfcf81..0a3b112acb3b00 100644 --- a/packages/react-components/file-type-icons-preview/library/src/utils/FileIconType.ts +++ b/packages/react-components/file-type-icons-preview/library/src/utils/FileIconType.ts @@ -29,27 +29,3 @@ export enum FileIconType { album = 21, listForm = 22, } - -export type FileIconTypeInput = - | 1 - | 2 - | 3 - | 4 - | 5 - | 6 - | 7 - | 8 - | 9 - | 10 - | 11 - | 12 - | 13 - | 14 - | 15 - | 16 - | 17 - | 18 - | 19 - | 20 - | 21 - | 22; diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts index 7ffed219a658b4..befb086339094f 100644 --- a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts +++ b/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts @@ -1,6 +1,5 @@ import { FileTypeIconMap } from './FileTypeIconMap'; import { FileIconType } from './FileIconType'; -import type { FileIconTypeInput } from './FileIconType'; let _extensionToIconName: { [key: string]: string }; let _typeToIconName: { [key: number]: string }; @@ -23,7 +22,7 @@ export interface IFileTypeIconOptions { * file type icons that are not associated with a file extension, * such as folder. */ - type?: FileIconTypeInput; + type?: FileIconType; /** * The size of the icon in pixels. * @default 16 diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx index a31c8f09ae04d4..8d454b4565d214 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx @@ -3,7 +3,7 @@ import type { JSXElement } from '@fluentui/react-components'; import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; import type { FileTypeIconProps } from '@fluentui/file-type-icons-preview'; -export const Default = (props: Partial): JSXElement => ; +export const Default = (args: FileTypeIconProps): JSXElement => ; Default.args = { extension: 'docx', diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx index 93a907a7b972b3..d70d435d3635a6 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx @@ -1,8 +1,7 @@ import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; import type { Meta } from '@storybook/react'; -export { Default } from './FileTypeIconDefault.stories'; -export { Sizes } from './FileTypeIconSizes.stories'; +export { Sizes as Default } from './FileTypeIconSizes.stories'; export { CommonFileTypes } from './FileTypeIconCommon.stories'; export { SpecialTypes } from './FileTypeIconSpecialTypes.stories'; export { UrlAndHtmlFunctions } from './FileTypeIconUrlAndHtml.stories'; From 71338bf0a05fa7c56469e60958987ea7625d748e Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 16:56:50 -0800 Subject: [PATCH 26/69] story condensation snapshot --- .../FileTypeIcon/FileTypeIconBestPractices.md | 46 +-- .../FileTypeIconBestPractices.stories.tsx | 25 ++ .../FileTypeIconCommon.stories.tsx | 29 +- .../FileTypeIconDefault.stories.tsx | 89 +++++- .../FileTypeIcon/FileTypeIconDescription.md | 6 +- .../FileTypeIconSizes.stories.tsx | 90 ------ .../FileTypeIconSpecialTypes.stories.tsx | 54 ---- .../FileTypeIconUrlAndHtml.stories.tsx | 262 +++++++++--------- .../src/FileTypeIcon/index.stories.tsx | 9 +- 9 files changed, 280 insertions(+), 330 deletions(-) create mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.stories.tsx delete mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx delete mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSpecialTypes.stories.tsx diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md index 5c361f463f1f2a..011c3eb39b47aa 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md @@ -1,52 +1,22 @@ -# FileTypeIcon Best Practices - -## Do +# FileTypeIcon best practices - **Use SVG format (default) for better scalability and smaller file sizes** - SVG icons scale perfectly at any size and are more performant than PNG. - **Choose appropriate sizes based on UI density**: - - Use 16-20px for compact interfaces, toolbars, and dense lists + - Use 16-20px for compact interfaces, breadcrumbs, and dense lists - Use 24-32px for standard list items and cards - Use 40-48px for featured content and file pickers - Use 64-96px only for hero sections or file upload zones -- **Provide meaningful context through surrounding UI** - File type icons work best when combined with file names, metadata, or labels. +- **Provide meaningful context through surrounding UI** - File type icons work best when combined with file names, metadata, or labels. This is especially valuable in search results or similar experiences where diverse item types may be present. - **Use special types for non-file entities** - Use `folder`, `sharedFolder`, `listItem`, `docset`, or `genericFile` types for appropriate contexts instead of trying to force file extensions. -- **Handle unknown file extensions gracefully** - The component automatically falls back to `genericFile` for unrecognized extensions. +- **Handle unknown file extensions gracefully** - The component automatically falls back to `genericFile` for unrecognized extensions, and is regularly updated to support new file types if they have a recognized icon. - **Consider accessibility** - The component provides default alt text, but ensure the surrounding context makes the file's purpose clear to all users. - **Use consistent sizes within the same UI context** - Mixing different icon sizes in a single list or grid creates visual inconsistency. -## Don't +## Things to avoid - **Don't use file type icons as the sole means of identifying files** - Always pair icons with file names or descriptions for better usability and accessibility. - **Don't use file type icons as primary navigation elements** - These icons are meant to represent file types, not actions or navigation destinations. - **Don't override the default alt text without good reason** - The component provides sensible defaults based on the file extension or type. -- **Don't use very large sizes (64px+) in dense lists or tables** - Large icons create excessive whitespace and reduce content density. -- **Don't mix PNG and SVG formats in the same UI context** - Stick to one format for visual consistency; prefer SVG unless you have specific compatibility requirements. -- **Don't forget to handle loading states** - When displaying many icons, consider performance implications and implement appropriate loading strategies. -- **Don't use file type icons for branding or decorative purposes** - These icons follow Fluent Design System conventions and are meant for functional file type representation. -- **Don't assume all file types have unique icons** - Many extensions share the same generic icon; rely on file names for precise identification. - -## Usage Guidelines - -### In Lists and Tables - -Use smaller sizes (16-24px) and ensure adequate spacing between icons and text labels for readability. - -### In File Upload Interfaces - -Use medium to large sizes (40-64px) to create clear drop zones and visual feedback during file selection. - -### In Search Results - -Combine icons with file metadata (size, date modified, path) to help users quickly identify files. - -### In Breadcrumbs and Navigation - -Use small sizes (16-20px) to maintain compact navigation while providing visual cues about file types. - -### With Custom Styling - -When applying custom styles, maintain the icon's aspect ratio and avoid distorting the visual design. - -### Performance Considerations - -When rendering many icons (100+), consider virtualization and lazy loading techniques to maintain smooth performance. +- **Don't use very large sizes (64px+) in dense lists or tables** - Large icons create excessive whitespace and reduce content density. Use thumbnail previews of the real file contents if available. +- **Don't use file type icons for branding or decorative purposes** - These icons follow Fluent Design System conventions and are meant for functional file type representation. Avoid non-square aspect ratios, custom styling overrides or distorting the visual design. +- **Don't assume all file types have unique icons** - Many extensions may share the same icon; rely on file names for precise identification. diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.stories.tsx new file mode 100644 index 00000000000000..6ef69ab533e035 --- /dev/null +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.stories.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import type { JSXElement } from '@fluentui/react-components'; + +import bestPracticesMd from './FileTypeIconBestPractices.md'; + +export const BestPractices = (): JSXElement => { + return <>; +}; + +BestPractices.storyName = 'Best Practices'; + +BestPractices.parameters = { + docs: { + description: { + story: bestPracticesMd, + }, + canvas: { + sourceState: 'hidden', + }, + }, + previewTabs: { + 'storybook/docs/panel': { hidden: true }, + }, + viewMode: 'docs', +}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx index 5616aebcdc96a3..9a60597c678e14 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; import { tokens } from '@fluentui/react-components'; -import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import { FileTypeIcon, FileIconType } from '@fluentui/file-type-icons-preview'; import { makeStyles, shorthands } from '@griffel/react'; const useStyles = makeStyles({ @@ -51,6 +51,14 @@ const fileTypeCategories = { data: ['zip', 'tar', 'sql', 'accdb', 'xml'], }; +const specialTypes = [ + { type: FileIconType.folder, label: 'Folder' }, + { type: FileIconType.genericFile, label: 'Generic File' }, + { type: FileIconType.sharedFolder, label: 'Shared Folder' }, + { type: FileIconType.listItem, label: 'List Item' }, + { type: FileIconType.docset, label: 'Docset' }, +]; + export const CommonFileTypes = (): JSXElement => { const styles = useStyles(); @@ -103,6 +111,21 @@ export const CommonFileTypes = (): JSXElement => { ))}
+ +
+
Special Types
+

+ These types are not based on file extensions, but represent objects like folders and list items. +

+
+ {specialTypes.map(({ type, label }) => ( +
+ +
{label}
+
+ ))} +
+
); }; @@ -111,7 +134,9 @@ CommonFileTypes.parameters = { docs: { description: { story: - 'FileTypeIcon supports a comprehensive library of common file extensions organized by category. This includes documents, spreadsheets, presentations, images, media files, web files, code files, archives, and database files. Each file type has a distinct, recognizable icon.', + 'FileTypeIcon supports a comprehensive library of file extensions organized by category (documents, media, code, data) and special types that are not based on file extensions (folder, shared folder, list item, docset, generic file).', }, }, }; + +CommonFileTypes.storyName = 'File Types'; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx index 8d454b4565d214..6b6772c7599a05 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx @@ -1,12 +1,87 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; -import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; -import type { FileTypeIconProps } from '@fluentui/file-type-icons-preview'; +import { tokens } from '@fluentui/react-components'; +import { FileIconType, FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import { makeStyles } from '@griffel/react'; -export const Default = (args: FileTypeIconProps): JSXElement => ; +const useStyles = makeStyles({ + container: { + display: 'flex', + flexDirection: 'column', + gap: '16px', + }, + iconGroup: { + display: 'flex', + alignItems: 'center', + gap: '16px', + }, + label: { + fontSize: tokens.fontSizeBase200, + fontWeight: tokens.fontWeightSemibold, + color: tokens.colorNeutralForeground2, + minWidth: '40px', + }, + description: { + fontSize: tokens.fontSizeBase100, + color: tokens.colorNeutralForeground3, + }, +}); -Default.args = { - extension: 'docx', - size: 48, - imageFileType: 'svg', +export const Default = (): JSXElement => { + const styles = useStyles(); + + return ( +
+
+ +
16px
+
Compact UIs, toolbars, dense tables. Example shows a Word document filetype icon.
+
+
+ +
20px
+
Compact lists, navigation items. Example shows a folder, referenced via FileIconType.
+
+
+ +
24px
+
Standard list items, search results. Example shows a PDF.
+
+
+ +
32px
+
Standard cards, file browsers. Example shows a PowerPoint presentation with a legacy file extension.
+
+
+ +
40px
+
Featured items, file pickers. Example shows a video file.
+
+
+ +
48px
+
Grid views, attachment previews. Example shows a Microsoft Lists object.
+
+
+ +
64px
+
Large grid items, file upload zones. Text file.
+
+
+ +
96px
+
Hero sections, large previews. Example shows a shared folder.
+
+
+ ); +}; + +Default.parameters = { + docs: { + description: { + story: + 'FileTypeIcon supports 8 size variants (16, 20, 24, 32, 40, 48, 64, and 96 pixels) to accommodate different UI contexts. Choose smaller sizes for compact interfaces and larger sizes for featured content or file upload experiences.', + }, + }, }; + diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md index d5b06be38d7e8c..1674419d5c509c 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md @@ -1,12 +1,10 @@ # FileTypeIcon -Filetype icons represent a file or other "digital object" based on its extension or a special type (like folder). The Fluent UI design system component helps you provide consistent, recognizable visual representations of the user's items and documents across your application aligned with Microsoft 365. +Filetype icons represent a file or other "digital object" based on its extension or a special type (like folder). This Fluent UI design system component provides consistent, recognizable visual representations of the user's items and documents across your application, aligned with Microsoft 365. The component automatically selects the appropriate icon from the comprehensive Fluent Design file type icon set and handles device pixel ratio for optimal display quality on all screens. -## When to use Filetype Icons - -Use FileTypeIcon when you need to: +## Use Filetype Icons when you need to: - **Display file lists or grids** - Help users quickly identify file types in recent lists, document libraries, file browsers, or search results - **Show file attachments or file upload interfaces** - Indicate attachment types in emails, messages, or forms. Provide visual feedback about accepted or uploaded file types diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx deleted file mode 100644 index 75b3fc6d985b40..00000000000000 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSizes.stories.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import * as React from 'react'; -import type { JSXElement } from '@fluentui/react-components'; -import { tokens } from '@fluentui/react-components'; -import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; -import { makeStyles } from '@griffel/react'; - -const useStyles = makeStyles({ - container: { - display: 'flex', - alignItems: 'flex-end', - gap: '24px', - flexWrap: 'wrap', - }, - iconGroup: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - gap: '8px', - }, - label: { - fontSize: tokens.fontSizeBase200, - fontWeight: tokens.fontWeightSemibold, - color: tokens.colorNeutralForeground2, - }, - description: { - fontSize: tokens.fontSizeBase100, - color: tokens.colorNeutralForeground3, - textAlign: 'center', - maxWidth: '120px', - }, -}); - -export const Sizes = (): JSXElement => { - const styles = useStyles(); - - return ( -
-
- -
16px
-
Compact UIs, toolbars, dense tables
-
-
- -
20px
-
Compact lists, navigation items
-
-
- -
24px
-
Standard list items, search results
-
-
- -
32px
-
Standard cards, file browsers
-
-
- -
40px
-
Featured items, file pickers
-
-
- -
48px
-
Grid views, attachment previews
-
-
- -
64px
-
Large grid items, file upload zones
-
-
- -
96px
-
Hero sections, large previews
-
-
- ); -}; - -Sizes.parameters = { - docs: { - description: { - story: - 'FileTypeIcon supports 8 size variants (16, 20, 24, 32, 40, 48, 64, and 96 pixels) to accommodate different UI contexts. Choose smaller sizes for compact interfaces and larger sizes for featured content or file upload experiences.', - }, - }, -}; - diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSpecialTypes.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSpecialTypes.stories.tsx deleted file mode 100644 index cb8433b1860928..00000000000000 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconSpecialTypes.stories.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import * as React from 'react'; -import type { JSXElement } from '@fluentui/react-components'; -import { FileTypeIcon, FileIconType } from '@fluentui/file-type-icons-preview'; -import { makeStyles } from '@griffel/react'; - -const useStyles = makeStyles({ - container: { - display: 'grid', - gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))', - gap: '16px', - }, - item: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - gap: '8px', - }, - label: { - fontSize: '12px', - textAlign: 'center', - }, -}); - -const specialTypes = [ - { type: FileIconType.folder, label: 'Folder' }, - { type: FileIconType.genericFile, label: 'Generic File' }, - { type: FileIconType.sharedFolder, label: 'Shared Folder' }, - { type: FileIconType.listItem, label: 'List Item' }, - { type: FileIconType.docset, label: 'Docset' }, -]; - -export const SpecialTypes = (): JSXElement => { - const styles = useStyles(); - - return ( -
- {specialTypes.map(({ type, label }) => ( -
- -
{label}
-
- ))} -
- ); -}; - -SpecialTypes.parameters = { - docs: { - description: { - story: - 'FileTypeIcon supports special file types that are not based on file extensions, such as folders and list items.', - }, - }, -}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx index 2fd255c9c24979..191f098399eeae 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx @@ -67,153 +67,155 @@ const useStyles = makeStyles({ const commonFileTypes = ['docx', 'pdf', 'xlsx']; -export const UrlAndHtmlFunctions = (): JSXElement => { - const styles = useStyles(); +export const UrlAndHtml = { + render: (): JSXElement => { + const styles = useStyles(); - return ( -
- {/* URL Function Demo */} -
-
getFileTypeIconAsUrl() - PNG Format with Different DPIs
-
- {commonFileTypes.map(extension => ( -
-
.{extension}
- {(['1x', '1.5x', '2x'] as const).map(dpi => { - const url = getFileTypeIconAsUrl({ - extension, - size: 48, - imageFileType: 'png', - }); + return ( +
+ {/* URL Function Demo */} +
+
getFileTypeIconAsUrl() - PNG Format with Different DPIs
+
+ {commonFileTypes.map(extension => ( +
+
.{extension}
+ {(['1x', '1.5x', '2x'] as const).map(dpi => { + const url = getFileTypeIconAsUrl({ + extension, + size: 48, + imageFileType: 'png', + }); - // For demo purposes, we'll show what the URL would be for each DPI - // In real usage, the browser's devicePixelRatio would determine this - const dpiSuffix = dpi === '1x' ? '' : `_${dpi}`; - const demoUrl = url?.replace(/48/, `48${dpiSuffix}`); + // For demo purposes, we'll show what the URL would be for each DPI + // In real usage, the browser's devicePixelRatio would determine this + const dpiSuffix = dpi === '1x' ? '' : `_${dpi}`; + const demoUrl = url?.replace(/48/, `48${dpiSuffix}`); - return ( -
-
{dpi} DPI:
-
{demoUrl}
-
- ); - })} -
- ))} + return ( +
+
{dpi} DPI:
+
{demoUrl}
+
+ ); + })} +
+ ))} +
-
- - {/* SVG URL Demo */} -
-
getFileTypeIconAsUrl() - SVG Format with Different DPIs
-
- {commonFileTypes.map(extension => ( -
-
.{extension}
- {(['1x', '1.5x', '2x'] as const).map(dpi => { - const url = getFileTypeIconAsUrl({ - extension, - size: 48, - imageFileType: 'svg', - }); - // SVG only uses 1.5x for specific DPI ranges, otherwise uses base - const dpiSuffix = dpi === '1.5x' ? '_1.5x' : ''; - const demoUrl = url?.replace(/48/, `48${dpiSuffix}`); + {/* SVG URL Demo */} +
+
getFileTypeIconAsUrl() - SVG Format with Different DPIs
+
+ {commonFileTypes.map(extension => ( +
+
.{extension}
+ {(['1x', '1.5x', '2x'] as const).map(dpi => { + const url = getFileTypeIconAsUrl({ + extension, + size: 48, + imageFileType: 'svg', + }); - return ( -
-
{dpi} DPI:
-
{demoUrl}
-
- ); - })} -
- ))} -
-
+ // SVG only uses 1.5x for specific DPI ranges, otherwise uses base + const dpiSuffix = dpi === '1.5x' ? '_1.5x' : ''; + const demoUrl = url?.replace(/48/, `48${dpiSuffix}`); - {/* HTML String Demo */} -
-
getFileTypeIconAsHTMLString() - Visual Preview
-
- {commonFileTypes.map(extension => ( -
-
.{extension}
-
-
-
-
HTML Output:
-
- {getFileTypeIconAsHTMLString({ - extension, - size: 48, - imageFileType: 'svg', + return ( +
+
{dpi} DPI:
+
{demoUrl}
+
+ ); })}
-
- ))} + ))} +
-
- {/* Comparison at Different Sizes */} -
-
getFileTypeIconAsHTMLString() - Different Sizes
-
- {([16, 24, 48] as const).map(size => ( -
-
Size: {size}px
- {commonFileTypes.map(extension => ( -
-
.{extension}:
-
-
-
+ {/* HTML String Demo */} +
+
getFileTypeIconAsHTMLString() - Visual Preview
+
+ {commonFileTypes.map(extension => ( +
+
.{extension}
+
+
+
+
HTML Output:
+
+ {getFileTypeIconAsHTMLString({ + extension, + size: 48, + imageFileType: 'svg', + })}
- ))} -
- ))} +
+ ))} +
-
- {/* Interactive Example */} -
-
Current Device Information
-
- Device Pixel Ratio: {typeof window !== 'undefined' ? window.devicePixelRatio : 'N/A'} + {/* Comparison at Different Sizes */} +
+
getFileTypeIconAsHTMLString() - Different Sizes
+
+ {([16, 24, 48] as const).map(size => ( +
+
Size: {size}px
+ {commonFileTypes.map(extension => ( +
+
.{extension}:
+
+
+
+
+ ))} +
+ ))} +
-
- This affects which icon variant is loaded for PNG images. SVG images scale better but may use 1.5x variant - for better rendering. + + {/* Interactive Example */} +
+
Current Device Information
+
+ Device Pixel Ratio: {typeof window !== 'undefined' ? window.devicePixelRatio : 'N/A'} +
+
+ This affects which icon variant is loaded for PNG images. SVG images scale better but may use 1.5x variant + for better rendering. +
-
- ); -}; - -UrlAndHtmlFunctions.parameters = { - docs: { - description: { - story: - 'Demonstrates the `getFileTypeIconAsUrl()` and `getFileTypeIconAsHTMLString()` utility functions with different DPI settings (1x, 1.5x, 2x) and common file types (docx, pdf, xlsx). These functions are useful when you need direct access to CDN URLs or HTML markup for file type icons.', + ); + }, + name: 'URL and HTML Functions', + parameters: { + docs: { + description: { + story: + 'Demonstrates the `getFileTypeIconAsUrl()` and `getFileTypeIconAsHTMLString()` utility functions with different DPI settings (1x, 1.5x, 2x) and common file types (docx, pdf, xlsx). These functions are useful when you need direct access to CDN URLs or HTML markup for file type icons.', + }, }, }, }; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx index d70d435d3635a6..2386679b4da3e6 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx @@ -1,15 +1,14 @@ import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; import type { Meta } from '@storybook/react'; -export { Sizes as Default } from './FileTypeIconSizes.stories'; +export { Default } from './FileTypeIconDefault.stories'; export { CommonFileTypes } from './FileTypeIconCommon.stories'; -export { SpecialTypes } from './FileTypeIconSpecialTypes.stories'; -export { UrlAndHtmlFunctions } from './FileTypeIconUrlAndHtml.stories'; +export { UrlAndHtml } from './FileTypeIconUrlAndHtml.stories'; +export { BestPractices } from './FileTypeIconBestPractices.stories'; export { Accessibility } from './FileTypeIconAccessibility.stories'; export { EdgeCases } from './FileTypeIconEdgeCases.stories'; import descriptionMd from './FileTypeIconDescription.md'; -import bestPracticesMd from './FileTypeIconBestPractices.md'; export default { title: 'Icons/Filetype Icons', @@ -17,7 +16,7 @@ export default { parameters: { docs: { description: { - component: [descriptionMd, bestPracticesMd].join('\n'), + component: descriptionMd, }, }, }, From 93a577bf34c6861df827f5a28ee94c85af14f370 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 17:12:44 -0800 Subject: [PATCH 27/69] condensation 2 --- .../FileTypeIcon/FileTypeIconBestPractices.md | 2 +- .../FileTypeIconBestPractices.stories.tsx | 25 ------------------- .../src/FileTypeIcon/index.stories.tsx | 8 +++--- 3 files changed, 5 insertions(+), 30 deletions(-) delete mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.stories.tsx diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md index 011c3eb39b47aa..f09ce54e1b468b 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md @@ -1,4 +1,4 @@ -# FileTypeIcon best practices +# Best practices - **Use SVG format (default) for better scalability and smaller file sizes** - SVG icons scale perfectly at any size and are more performant than PNG. - **Choose appropriate sizes based on UI density**: diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.stories.tsx deleted file mode 100644 index 6ef69ab533e035..00000000000000 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.stories.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from 'react'; -import type { JSXElement } from '@fluentui/react-components'; - -import bestPracticesMd from './FileTypeIconBestPractices.md'; - -export const BestPractices = (): JSXElement => { - return <>; -}; - -BestPractices.storyName = 'Best Practices'; - -BestPractices.parameters = { - docs: { - description: { - story: bestPracticesMd, - }, - canvas: { - sourceState: 'hidden', - }, - }, - previewTabs: { - 'storybook/docs/panel': { hidden: true }, - }, - viewMode: 'docs', -}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx index 2386679b4da3e6..2bad32ff9f38ec 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx @@ -1,22 +1,22 @@ import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; import type { Meta } from '@storybook/react'; +import descriptionMd from './FileTypeIconDescription.md'; +import bestPracticesMd from './FileTypeIconBestPractices.md'; + export { Default } from './FileTypeIconDefault.stories'; export { CommonFileTypes } from './FileTypeIconCommon.stories'; export { UrlAndHtml } from './FileTypeIconUrlAndHtml.stories'; -export { BestPractices } from './FileTypeIconBestPractices.stories'; export { Accessibility } from './FileTypeIconAccessibility.stories'; export { EdgeCases } from './FileTypeIconEdgeCases.stories'; -import descriptionMd from './FileTypeIconDescription.md'; - export default { title: 'Icons/Filetype Icons', component: FileTypeIcon, parameters: { docs: { description: { - component: descriptionMd, + component: [descriptionMd, bestPracticesMd].join('\n'), }, }, }, From 3d484aedfbb7c897633214a4418ef063badb3d79 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 17:23:03 -0800 Subject: [PATCH 28/69] improving alt tag generation in the control itself, removing a11y story since it doesnt add much --- .../FileTypeIcon/useFileTypeIcon.ts | 6 +- .../FileTypeIconAccessibility.stories.tsx | 217 ------------------ .../src/FileTypeIcon/index.stories.tsx | 1 - 3 files changed, 5 insertions(+), 219 deletions(-) delete mode 100644 packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconAccessibility.stories.tsx diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts index a72e21a594e51b..9be1f7105007b4 100644 --- a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts +++ b/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts @@ -6,6 +6,7 @@ import { getFileTypeIconSuffix, DEFAULT_ICON_SIZE, } from '../../utils/getFileTypeIconProps'; +import { FileIconType } from '../../utils/FileIconType'; const DEFAULT_BASE_URL = 'https://res.cdn.office.net/files/fabric-cdn-prod_20251119.001/assets/item-types/'; @@ -42,6 +43,9 @@ export const useFileTypeIcon_unstable = ( iconUrl = `${baseUrl}${size}/${baseIconName}.${imageFileType}`; } + // Generate alt text: use extension if provided, otherwise get the enum name for type + const altText = extension || (type !== undefined ? FileIconType[type] : ''); + const state: FileTypeIconState = { size, imageFileType, @@ -53,7 +57,7 @@ export const useFileTypeIcon_unstable = ( getIntrinsicElementProps('img', { ref, src: iconUrl, - alt: extension || 'File type icon', + alt: `${altText} file icon`, ...props, // Remove our custom props from being passed to the img element extension: undefined, diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconAccessibility.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconAccessibility.stories.tsx deleted file mode 100644 index 68b1be69558809..00000000000000 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconAccessibility.stories.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import * as React from 'react'; -import type { JSXElement } from '@fluentui/react-components'; -import { tokens } from '@fluentui/react-components'; -import { FileTypeIcon, FileIconType } from '@fluentui/file-type-icons-preview'; -import { makeStyles, shorthands } from '@griffel/react'; - -const useStyles = makeStyles({ - container: { - display: 'flex', - flexDirection: 'column', - gap: '32px', - }, - section: { - display: 'flex', - flexDirection: 'column', - gap: '12px', - }, - sectionTitle: { - fontSize: tokens.fontSizeBase400, - fontWeight: tokens.fontWeightSemibold, - marginBottom: '8px', - }, - description: { - fontSize: tokens.fontSizeBase300, - color: tokens.colorNeutralForeground3, - marginBottom: '8px', - }, - exampleRow: { - display: 'flex', - alignItems: 'center', - gap: '16px', - ...shorthands.padding('12px'), - backgroundColor: tokens.colorNeutralBackground1, - ...shorthands.borderRadius(tokens.borderRadiusMedium), - }, - fileInfo: { - display: 'flex', - flexDirection: 'column', - gap: '4px', - }, - fileName: { - fontSize: tokens.fontSizeBase300, - fontWeight: tokens.fontWeightSemibold, - }, - fileDetails: { - fontSize: tokens.fontSizeBase200, - color: tokens.colorNeutralForeground3, - }, - codeBlock: { - backgroundColor: tokens.colorNeutralBackground3, - ...shorthands.padding('8px', '12px'), - ...shorthands.borderRadius(tokens.borderRadiusMedium), - fontFamily: tokens.fontFamilyMonospace, - fontSize: tokens.fontSizeBase200, - overflowX: 'auto', - }, - highContrastDemo: { - '@media (forced-colors: active)': { - ...shorthands.border('2px', 'solid', 'CanvasText'), - ...shorthands.padding('16px'), - ...shorthands.borderRadius(tokens.borderRadiusMedium), - }, - }, - listExample: { - display: 'flex', - flexDirection: 'column', - gap: '8px', - }, - listItem: { - display: 'flex', - alignItems: 'center', - gap: '12px', - ...shorthands.padding('8px', '12px'), - backgroundColor: tokens.colorNeutralBackground1, - ...shorthands.borderRadius(tokens.borderRadiusMedium), - ':hover': { - backgroundColor: tokens.colorNeutralBackground1Hover, - }, - ':focus-within': { - ...shorthands.outline('2px', 'solid', tokens.colorStrokeFocus2), - outlineOffset: '2px', - }, - }, - link: { - textDecoration: 'none', - color: 'inherit', - display: 'flex', - alignItems: 'center', - gap: '12px', - flex: 1, - ':focus': { - ...shorthands.outline('none'), - }, - }, -}); - -export const Accessibility = (): JSXElement => { - const styles = useStyles(); - - return ( -
-
-
Default Alt Text
-
- FileTypeIcon automatically generates descriptive alt text based on the file extension or type. This ensures - screen readers can convey the file type to users. -
-
- -
-
Annual Report.pdf
-
Alt text: "pdf file icon"
-
-
-
- -
-
Documents
-
Alt text: "folder file icon"
-
-
- - {` -// Rendered with alt="pdf file icon"`} - -
- -
-
Context with File Names
-
- Always pair file type icons with file names or labels. The icon provides visual recognition while the text - ensures accessibility for all users. -
-
-
- - Project Proposal.docx -
-
- - Budget 2025.xlsx -
-
- - Q4 Presentation.pptx -
-
-
- -
-
Keyboard Navigation
-
- When icons are part of interactive elements, ensure proper keyboard navigation and focus indicators are - present. -
- -
- Try pressing Tab to navigate through the list above. Focus indicators clearly show the active item. -
-
- -
-
High Contrast Mode Support
-
- FileTypeIcon respects Windows High Contrast mode and other forced-colors settings, ensuring icons remain - visible and distinguishable. -
-
- - - - - -
-
- Icons automatically adapt to high contrast themes while maintaining their recognizable shapes. -
-
- -
-
Screen Reader Announcements
-
- When using icons in dynamic content (like file upload feedback), ensure screen readers announce changes - appropriately using ARIA live regions. -
- - {`
- - Report.pdf uploaded successfully -
`} -
-
-
- ); -}; - -Accessibility.parameters = { - docs: { - description: { - story: - 'FileTypeIcon includes built-in accessibility features like automatic alt text generation, support for high contrast mode, and compatibility with screen readers. Always provide surrounding context (file names, labels) to ensure all users can identify files effectively.', - }, - }, -}; diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx index 2bad32ff9f38ec..1ab5a0f6625dbe 100644 --- a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx +++ b/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx @@ -7,7 +7,6 @@ import bestPracticesMd from './FileTypeIconBestPractices.md'; export { Default } from './FileTypeIconDefault.stories'; export { CommonFileTypes } from './FileTypeIconCommon.stories'; export { UrlAndHtml } from './FileTypeIconUrlAndHtml.stories'; -export { Accessibility } from './FileTypeIconAccessibility.stories'; export { EdgeCases } from './FileTypeIconEdgeCases.stories'; export default { From 199803c64bdd8e393969b9bbc6b9323483f425e4 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 17:49:58 -0800 Subject: [PATCH 29/69] ran all pre-checks from plan-testingAndPromotionEnhanced. ready to start package rename --- .../etc/file-type-icons-preview.api.md | 28 +++++++++---------- .../library/src/index.ts | 2 +- .../library/src/utils/getFileTypeIconAsUrl.ts | 6 ++-- .../library/src/utils/getFileTypeIconProps.ts | 8 +++--- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md b/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md index 1b3c1b7301f54a..331805b238c088 100644 --- a/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md +++ b/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md @@ -67,9 +67,6 @@ export enum FileIconType { todoItem = 19 } -// @public (undocumented) -export type FileIconTypeInput = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22; - // @public export const FileTypeIcon: ForwardRefComponent; @@ -80,13 +77,22 @@ export const fileTypeIconClassNames: SlotClassNames; export const FileTypeIconMap: { [key: string]: { extensions?: string[]; + types?: FileIconType[]; }; }; +// @public (undocumented) +export interface FileTypeIconOptions { + extension?: string; + imageFileType?: ImageFileType; + size?: FileTypeIconSize; + type?: FileIconType; +} + // @public (undocumented) export type FileTypeIconProps = ComponentProps & { extension?: string; - type?: FileIconTypeInput; + type?: FileIconType; size?: FileTypeIconSize; imageFileType?: ImageFileType; baseUrl?: string; @@ -106,16 +112,16 @@ export type FileTypeIconState = ComponentState & Required Date: Mon, 24 Nov 2025 17:58:03 -0800 Subject: [PATCH 30/69] package move to remove -preview suffix --- .../library/.babelrc.json | 0 .../library/.swcrc | 0 .../library/LICENSE | 0 .../library/MIGRATION.md | 0 .../library/README.md | 0 .../library/bundle-size/FileTypeIcon.fixture.js | 0 .../library/config/api-extractor.json | 0 .../library/config/tests.js | 0 .../library/eslint.config.js | 0 .../library/etc/file-type-icons-preview.api.md | 0 .../library/jest.config.js | 0 .../library/package.json | 0 .../library/project.json | 0 .../library/src/FileTypeIcon.ts | 0 .../library/src/components/FileTypeIcon/FileTypeIcon.test.tsx | 0 .../library/src/components/FileTypeIcon/FileTypeIcon.tsx | 0 .../library/src/components/FileTypeIcon/FileTypeIcon.types.ts | 0 .../library/src/components/FileTypeIcon/index.ts | 0 .../library/src/components/FileTypeIcon/renderFileTypeIcon.tsx | 0 .../library/src/components/FileTypeIcon/useFileTypeIcon.ts | 0 .../src/components/FileTypeIcon/useFileTypeIconStyles.styles.ts | 0 .../library/src/index.ts | 0 .../library/src/testing/isConformant.ts | 0 .../library/src/utils/FileIconType.ts | 0 .../library/src/utils/FileTypeIconMap.ts | 0 .../library/src/utils/getFileTypeIconAsUrl.test.ts | 0 .../library/src/utils/getFileTypeIconAsUrl.ts | 0 .../library/src/utils/getFileTypeIconProps.ts | 0 .../library/src/utils/initializeFileTypeIcons.tsx | 0 .../library/tsconfig.json | 0 .../library/tsconfig.lib.json | 0 .../library/tsconfig.spec.json | 0 .../stories/.storybook/main.js | 0 .../stories/.storybook/preview.js | 0 .../stories/.storybook/tsconfig.json | 0 .../stories/README.md | 0 .../stories/eslint.config.js | 0 .../stories/package.json | 0 .../stories/project.json | 0 .../stories/src/.gitkeep | 0 .../stories/src/FileTypeIcon/FileTypeIconBestPractices.md | 0 .../stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx | 0 .../stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx | 0 .../stories/src/FileTypeIcon/FileTypeIconDescription.md | 0 .../stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx | 0 .../stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx | 0 .../stories/src/FileTypeIcon/index.stories.tsx | 0 .../stories/src/index.ts | 0 .../stories/tsconfig.json | 0 .../stories/tsconfig.lib.json | 0 50 files changed, 0 insertions(+), 0 deletions(-) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/.babelrc.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/.swcrc (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/LICENSE (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/MIGRATION.md (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/README.md (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/bundle-size/FileTypeIcon.fixture.js (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/config/api-extractor.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/config/tests.js (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/eslint.config.js (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/etc/file-type-icons-preview.api.md (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/jest.config.js (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/package.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/project.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/FileTypeIcon.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/components/FileTypeIcon/FileTypeIcon.tsx (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/components/FileTypeIcon/FileTypeIcon.types.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/components/FileTypeIcon/index.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/components/FileTypeIcon/renderFileTypeIcon.tsx (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/components/FileTypeIcon/useFileTypeIcon.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/components/FileTypeIcon/useFileTypeIconStyles.styles.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/index.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/testing/isConformant.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/utils/FileIconType.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/utils/FileTypeIconMap.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/utils/getFileTypeIconAsUrl.test.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/utils/getFileTypeIconAsUrl.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/utils/getFileTypeIconProps.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/src/utils/initializeFileTypeIcons.tsx (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/tsconfig.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/tsconfig.lib.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/library/tsconfig.spec.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/.storybook/main.js (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/.storybook/preview.js (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/.storybook/tsconfig.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/README.md (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/eslint.config.js (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/package.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/project.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/src/.gitkeep (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/src/FileTypeIcon/FileTypeIconBestPractices.md (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/src/FileTypeIcon/FileTypeIconDescription.md (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/src/FileTypeIcon/index.stories.tsx (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/src/index.ts (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/tsconfig.json (100%) rename packages/react-components/{file-type-icons-preview => react-file-type-icons}/stories/tsconfig.lib.json (100%) diff --git a/packages/react-components/file-type-icons-preview/library/.babelrc.json b/packages/react-components/react-file-type-icons/library/.babelrc.json similarity index 100% rename from packages/react-components/file-type-icons-preview/library/.babelrc.json rename to packages/react-components/react-file-type-icons/library/.babelrc.json diff --git a/packages/react-components/file-type-icons-preview/library/.swcrc b/packages/react-components/react-file-type-icons/library/.swcrc similarity index 100% rename from packages/react-components/file-type-icons-preview/library/.swcrc rename to packages/react-components/react-file-type-icons/library/.swcrc diff --git a/packages/react-components/file-type-icons-preview/library/LICENSE b/packages/react-components/react-file-type-icons/library/LICENSE similarity index 100% rename from packages/react-components/file-type-icons-preview/library/LICENSE rename to packages/react-components/react-file-type-icons/library/LICENSE diff --git a/packages/react-components/file-type-icons-preview/library/MIGRATION.md b/packages/react-components/react-file-type-icons/library/MIGRATION.md similarity index 100% rename from packages/react-components/file-type-icons-preview/library/MIGRATION.md rename to packages/react-components/react-file-type-icons/library/MIGRATION.md diff --git a/packages/react-components/file-type-icons-preview/library/README.md b/packages/react-components/react-file-type-icons/library/README.md similarity index 100% rename from packages/react-components/file-type-icons-preview/library/README.md rename to packages/react-components/react-file-type-icons/library/README.md diff --git a/packages/react-components/file-type-icons-preview/library/bundle-size/FileTypeIcon.fixture.js b/packages/react-components/react-file-type-icons/library/bundle-size/FileTypeIcon.fixture.js similarity index 100% rename from packages/react-components/file-type-icons-preview/library/bundle-size/FileTypeIcon.fixture.js rename to packages/react-components/react-file-type-icons/library/bundle-size/FileTypeIcon.fixture.js diff --git a/packages/react-components/file-type-icons-preview/library/config/api-extractor.json b/packages/react-components/react-file-type-icons/library/config/api-extractor.json similarity index 100% rename from packages/react-components/file-type-icons-preview/library/config/api-extractor.json rename to packages/react-components/react-file-type-icons/library/config/api-extractor.json diff --git a/packages/react-components/file-type-icons-preview/library/config/tests.js b/packages/react-components/react-file-type-icons/library/config/tests.js similarity index 100% rename from packages/react-components/file-type-icons-preview/library/config/tests.js rename to packages/react-components/react-file-type-icons/library/config/tests.js diff --git a/packages/react-components/file-type-icons-preview/library/eslint.config.js b/packages/react-components/react-file-type-icons/library/eslint.config.js similarity index 100% rename from packages/react-components/file-type-icons-preview/library/eslint.config.js rename to packages/react-components/react-file-type-icons/library/eslint.config.js diff --git a/packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md b/packages/react-components/react-file-type-icons/library/etc/file-type-icons-preview.api.md similarity index 100% rename from packages/react-components/file-type-icons-preview/library/etc/file-type-icons-preview.api.md rename to packages/react-components/react-file-type-icons/library/etc/file-type-icons-preview.api.md diff --git a/packages/react-components/file-type-icons-preview/library/jest.config.js b/packages/react-components/react-file-type-icons/library/jest.config.js similarity index 100% rename from packages/react-components/file-type-icons-preview/library/jest.config.js rename to packages/react-components/react-file-type-icons/library/jest.config.js diff --git a/packages/react-components/file-type-icons-preview/library/package.json b/packages/react-components/react-file-type-icons/library/package.json similarity index 100% rename from packages/react-components/file-type-icons-preview/library/package.json rename to packages/react-components/react-file-type-icons/library/package.json diff --git a/packages/react-components/file-type-icons-preview/library/project.json b/packages/react-components/react-file-type-icons/library/project.json similarity index 100% rename from packages/react-components/file-type-icons-preview/library/project.json rename to packages/react-components/react-file-type-icons/library/project.json diff --git a/packages/react-components/file-type-icons-preview/library/src/FileTypeIcon.ts b/packages/react-components/react-file-type-icons/library/src/FileTypeIcon.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/FileTypeIcon.ts rename to packages/react-components/react-file-type-icons/library/src/FileTypeIcon.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx rename to packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.tsx b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.tsx similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.tsx rename to packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.tsx diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.types.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/FileTypeIcon.types.ts rename to packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.types.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/index.ts b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/index.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/index.ts rename to packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/index.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/renderFileTypeIcon.tsx b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/renderFileTypeIcon.tsx similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/renderFileTypeIcon.tsx rename to packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/renderFileTypeIcon.tsx diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIcon.ts rename to packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIconStyles.styles.ts b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIconStyles.styles.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/components/FileTypeIcon/useFileTypeIconStyles.styles.ts rename to packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIconStyles.styles.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/index.ts b/packages/react-components/react-file-type-icons/library/src/index.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/index.ts rename to packages/react-components/react-file-type-icons/library/src/index.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/testing/isConformant.ts b/packages/react-components/react-file-type-icons/library/src/testing/isConformant.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/testing/isConformant.ts rename to packages/react-components/react-file-type-icons/library/src/testing/isConformant.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/FileIconType.ts b/packages/react-components/react-file-type-icons/library/src/utils/FileIconType.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/utils/FileIconType.ts rename to packages/react-components/react-file-type-icons/library/src/utils/FileIconType.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/FileTypeIconMap.ts b/packages/react-components/react-file-type-icons/library/src/utils/FileTypeIconMap.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/utils/FileTypeIconMap.ts rename to packages/react-components/react-file-type-icons/library/src/utils/FileTypeIconMap.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.test.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.test.ts rename to packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconAsUrl.ts rename to packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/utils/getFileTypeIconProps.ts rename to packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts diff --git a/packages/react-components/file-type-icons-preview/library/src/utils/initializeFileTypeIcons.tsx b/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.tsx similarity index 100% rename from packages/react-components/file-type-icons-preview/library/src/utils/initializeFileTypeIcons.tsx rename to packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.tsx diff --git a/packages/react-components/file-type-icons-preview/library/tsconfig.json b/packages/react-components/react-file-type-icons/library/tsconfig.json similarity index 100% rename from packages/react-components/file-type-icons-preview/library/tsconfig.json rename to packages/react-components/react-file-type-icons/library/tsconfig.json diff --git a/packages/react-components/file-type-icons-preview/library/tsconfig.lib.json b/packages/react-components/react-file-type-icons/library/tsconfig.lib.json similarity index 100% rename from packages/react-components/file-type-icons-preview/library/tsconfig.lib.json rename to packages/react-components/react-file-type-icons/library/tsconfig.lib.json diff --git a/packages/react-components/file-type-icons-preview/library/tsconfig.spec.json b/packages/react-components/react-file-type-icons/library/tsconfig.spec.json similarity index 100% rename from packages/react-components/file-type-icons-preview/library/tsconfig.spec.json rename to packages/react-components/react-file-type-icons/library/tsconfig.spec.json diff --git a/packages/react-components/file-type-icons-preview/stories/.storybook/main.js b/packages/react-components/react-file-type-icons/stories/.storybook/main.js similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/.storybook/main.js rename to packages/react-components/react-file-type-icons/stories/.storybook/main.js diff --git a/packages/react-components/file-type-icons-preview/stories/.storybook/preview.js b/packages/react-components/react-file-type-icons/stories/.storybook/preview.js similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/.storybook/preview.js rename to packages/react-components/react-file-type-icons/stories/.storybook/preview.js diff --git a/packages/react-components/file-type-icons-preview/stories/.storybook/tsconfig.json b/packages/react-components/react-file-type-icons/stories/.storybook/tsconfig.json similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/.storybook/tsconfig.json rename to packages/react-components/react-file-type-icons/stories/.storybook/tsconfig.json diff --git a/packages/react-components/file-type-icons-preview/stories/README.md b/packages/react-components/react-file-type-icons/stories/README.md similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/README.md rename to packages/react-components/react-file-type-icons/stories/README.md diff --git a/packages/react-components/file-type-icons-preview/stories/eslint.config.js b/packages/react-components/react-file-type-icons/stories/eslint.config.js similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/eslint.config.js rename to packages/react-components/react-file-type-icons/stories/eslint.config.js diff --git a/packages/react-components/file-type-icons-preview/stories/package.json b/packages/react-components/react-file-type-icons/stories/package.json similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/package.json rename to packages/react-components/react-file-type-icons/stories/package.json diff --git a/packages/react-components/file-type-icons-preview/stories/project.json b/packages/react-components/react-file-type-icons/stories/project.json similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/project.json rename to packages/react-components/react-file-type-icons/stories/project.json diff --git a/packages/react-components/file-type-icons-preview/stories/src/.gitkeep b/packages/react-components/react-file-type-icons/stories/src/.gitkeep similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/src/.gitkeep rename to packages/react-components/react-file-type-icons/stories/src/.gitkeep diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconBestPractices.md similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconBestPractices.md rename to packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconBestPractices.md diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx rename to packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx rename to packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDescription.md similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconDescription.md rename to packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDescription.md diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx rename to packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx rename to packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx diff --git a/packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/src/FileTypeIcon/index.stories.tsx rename to packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx diff --git a/packages/react-components/file-type-icons-preview/stories/src/index.ts b/packages/react-components/react-file-type-icons/stories/src/index.ts similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/src/index.ts rename to packages/react-components/react-file-type-icons/stories/src/index.ts diff --git a/packages/react-components/file-type-icons-preview/stories/tsconfig.json b/packages/react-components/react-file-type-icons/stories/tsconfig.json similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/tsconfig.json rename to packages/react-components/react-file-type-icons/stories/tsconfig.json diff --git a/packages/react-components/file-type-icons-preview/stories/tsconfig.lib.json b/packages/react-components/react-file-type-icons/stories/tsconfig.lib.json similarity index 100% rename from packages/react-components/file-type-icons-preview/stories/tsconfig.lib.json rename to packages/react-components/react-file-type-icons/stories/tsconfig.lib.json From 092682f7c2bd0a60d487330c6300358a694b2a3f Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 18:16:43 -0800 Subject: [PATCH 31/69] first test passed after package rename --- .github/CODEOWNERS | 4 ++-- .../react-components/package.json | 2 +- .../react-file-type-icons/library/LICENSE | 2 +- .../react-file-type-icons/library/MIGRATION.md | 17 ++++++++--------- .../react-file-type-icons/library/README.md | 10 ++++------ .../library/bundle-size/FileTypeIcon.fixture.js | 2 +- .../library/etc/file-type-icons-preview.api.md | 2 +- .../library/jest.config.js | 2 +- .../react-file-type-icons/library/package.json | 2 +- .../react-file-type-icons/library/project.json | 4 ++-- .../react-file-type-icons/stories/README.md | 6 +++--- .../react-file-type-icons/stories/package.json | 4 ++-- .../react-file-type-icons/stories/project.json | 4 ++-- .../FileTypeIcon/FileTypeIconCommon.stories.tsx | 2 +- .../FileTypeIconDefault.stories.tsx | 2 +- .../FileTypeIconEdgeCases.stories.tsx | 2 +- .../FileTypeIconUrlAndHtml.stories.tsx | 2 +- .../stories/src/FileTypeIcon/index.stories.tsx | 2 +- packages/react-file-type-icons/project.json | 2 +- tsconfig.base.all.json | 6 +++--- tsconfig.base.json | 6 +++--- 21 files changed, 41 insertions(+), 44 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4368f1d6ac6561..bb9d1c0d0e0175 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -331,8 +331,8 @@ packages/react-components/component-selector-preview/library @microsoft/teams-pr packages/react-components/component-selector-preview/stories @microsoft/teams-prg packages/react-components/react-menu-grid-preview/library @microsoft/teams-prg packages/react-components/react-menu-grid-preview/stories @microsoft/teams-prg -packages/react-components/file-type-icons-preview/library @microsoft/cxe-red -packages/react-components/file-type-icons-preview/stories @microsoft/cxe-red +packages/react-components/react-file-type-icons/library @microsoft/cxe-red @jahnp @bigbadcapers +packages/react-components/react-file-type-icons/stories @microsoft/cxe-red @jahnp @bigbadcapers # <%= NX-CODEOWNER-PLACEHOLDER %> # Deprecated v9 packages - exposed as part of `/unstable` api diff --git a/packages/react-components/react-components/package.json b/packages/react-components/react-components/package.json index 7af6d6e3be332e..5a563bee9b8ce3 100644 --- a/packages/react-components/react-components/package.json +++ b/packages/react-components/react-components/package.json @@ -78,7 +78,7 @@ "@fluentui/react-carousel": "^9.8.12", "@fluentui/react-color-picker": "^9.2.11", "@fluentui/react-nav": "^9.3.14", - "@fluentui/file-type-icons-preview": "^0.0.1" + "@fluentui/react-file-type-icons": "^0.0.1" }, "peerDependencies": { "@types/react": ">=16.14.0 <20.0.0", diff --git a/packages/react-components/react-file-type-icons/library/LICENSE b/packages/react-components/react-file-type-icons/library/LICENSE index ebc6d4c8e5118f..d2bf0af9ab9baf 100644 --- a/packages/react-components/react-file-type-icons/library/LICENSE +++ b/packages/react-components/react-file-type-icons/library/LICENSE @@ -1,4 +1,4 @@ -@fluentui/file-type-icons-preview +@fluentui/react-file-type-icons Copyright (c) Microsoft Corporation diff --git a/packages/react-components/react-file-type-icons/library/MIGRATION.md b/packages/react-components/react-file-type-icons/library/MIGRATION.md index b8caab721e2829..afa43598837e62 100644 --- a/packages/react-components/react-file-type-icons/library/MIGRATION.md +++ b/packages/react-components/react-file-type-icons/library/MIGRATION.md @@ -1,6 +1,6 @@ -# Migration Guide: react-file-type-icons to react-file-type-icons-preview +# Migration Guide: react-file-type-icons to react-file-type-icons -This guide helps you migrate from `@fluentui/react-file-type-icons` (v8) to `@fluentui/react-file-type-icons-preview` (v9). +This guide helps you migrate from `@fluentui/react-file-type-icons` (v8) to `@fluentui/react-file-type-icons` (v9). ## Overview @@ -15,8 +15,7 @@ The v9 version provides a **modern React component-based API** while maintaining ## Installation ```bash -npm uninstall @fluentui/react-file-type-icons -npm install @fluentui/react-file-type-icons-preview +npm install @fluentui/react-file-type-icons ``` ## What's Changed @@ -26,7 +25,7 @@ npm install @fluentui/react-file-type-icons-preview The only breaking change is the package name: - **Old**: `@fluentui/react-file-type-icons` -- **New**: `@fluentui/react-file-type-icons-preview` +- **New**: `@fluentui/react-file-type-icons` ### New Component API (Recommended) @@ -42,7 +41,7 @@ const iconProps = getFileTypeIconProps({ extension: 'docx', size: 48 }); **v9 (component-based) - Recommended:** ```tsx -import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; +import { FileTypeIcon } from '@fluentui/react-file-type-icons'; ; ``` @@ -72,7 +71,7 @@ function DocumentItem({ filename }) { **After:** ```tsx -import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; +import { FileTypeIcon } from '@fluentui/react-file-type-icons'; function DocumentItem({ filename }) { const extension = filename.split('.').pop(); @@ -100,7 +99,7 @@ const iconProps = getFileTypeIconProps({ type: FileIconType.folder, size: 48 }); **After:** ```tsx -import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-preview'; +import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons'; ; ``` @@ -113,7 +112,7 @@ import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-prev import { FileTypeIcon, // v9 component (new) getFileTypeIconAsUrl, // v8 utility (still works) -} from '@fluentui/react-file-type-icons-preview'; +} from '@fluentui/react-file-type-icons'; function MyComponent() { return ( diff --git a/packages/react-components/react-file-type-icons/library/README.md b/packages/react-components/react-file-type-icons/library/README.md index bad0f3b4d9a11b..b8eac1168c1740 100644 --- a/packages/react-components/react-file-type-icons/library/README.md +++ b/packages/react-components/react-file-type-icons/library/README.md @@ -1,9 +1,7 @@ -# @fluentui/file-type-icons-preview +# @fluentui/react-file-type-icons **File Type Icons components for [Fluent UI React](https://react.fluentui.dev/)** -These are not production-ready components and **should never be used in product**. This space is useful for testing new components whose APIs might change before final release. - ## Components ### FileTypeIcon @@ -23,7 +21,7 @@ Displays an icon representing a file type based on its extension or a special ty ### Basic Examples ```tsx -import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons-preview'; +import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons'; // File extension @@ -83,7 +81,7 @@ const iconProps = getFileTypeIconProps({ extension: 'docx', size: 48 }); **v9:** ```tsx -import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; +import { FileTypeIcon } from '@fluentui/react-file-type-icons'; ; ``` @@ -92,7 +90,7 @@ import { FileTypeIcon } from '@fluentui/react-file-type-icons-preview'; Underlying utilities are exported for advanced use cases: ```tsx -import { getFileTypeIconProps, getFileTypeIconAsUrl, FileIconType } from '@fluentui/react-file-type-icons-preview'; +import { getFileTypeIconProps, getFileTypeIconAsUrl, FileIconType } from '@fluentui/react-file-type-icons'; // Get icon URL const url = getFileTypeIconAsUrl({ extension: 'docx', size: 48 }); diff --git a/packages/react-components/react-file-type-icons/library/bundle-size/FileTypeIcon.fixture.js b/packages/react-components/react-file-type-icons/library/bundle-size/FileTypeIcon.fixture.js index a22fdd167bf53c..5e374a14cd3a02 100644 --- a/packages/react-components/react-file-type-icons/library/bundle-size/FileTypeIcon.fixture.js +++ b/packages/react-components/react-file-type-icons/library/bundle-size/FileTypeIcon.fixture.js @@ -1,4 +1,4 @@ -import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import { FileTypeIcon } from '@fluentui/react-file-type-icons'; console.log(FileTypeIcon); diff --git a/packages/react-components/react-file-type-icons/library/etc/file-type-icons-preview.api.md b/packages/react-components/react-file-type-icons/library/etc/file-type-icons-preview.api.md index 331805b238c088..33f598e1176e29 100644 --- a/packages/react-components/react-file-type-icons/library/etc/file-type-icons-preview.api.md +++ b/packages/react-components/react-file-type-icons/library/etc/file-type-icons-preview.api.md @@ -1,4 +1,4 @@ -## API Report File for "@fluentui/file-type-icons-preview" +## API Report File for "@fluentui/react-file-type-icons" > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). diff --git a/packages/react-components/react-file-type-icons/library/jest.config.js b/packages/react-components/react-file-type-icons/library/jest.config.js index b162ec0d59415e..8946bffacce655 100644 --- a/packages/react-components/react-file-type-icons/library/jest.config.js +++ b/packages/react-components/react-file-type-icons/library/jest.config.js @@ -23,7 +23,7 @@ if (swcJestConfig.swcrc === undefined) { * @type {import('@jest/types').Config.InitialOptions} */ module.exports = { - displayName: 'file-type-icons-preview', + displayName: 'react-file-type-icons', preset: '../../../../jest.preset.js', transform: { '^.+\\.tsx?$': ['@swc/jest', swcJestConfig], diff --git a/packages/react-components/react-file-type-icons/library/package.json b/packages/react-components/react-file-type-icons/library/package.json index 53252c869c1a5c..b339ac524adfae 100644 --- a/packages/react-components/react-file-type-icons/library/package.json +++ b/packages/react-components/react-file-type-icons/library/package.json @@ -1,5 +1,5 @@ { - "name": "@fluentui/file-type-icons-preview", + "name": "@fluentui/react-file-type-icons", "version": "0.0.0", "private": true, "description": "New fluentui react package", diff --git a/packages/react-components/react-file-type-icons/library/project.json b/packages/react-components/react-file-type-icons/library/project.json index a2facb7b2e2771..227a8e1ad8d90e 100644 --- a/packages/react-components/react-file-type-icons/library/project.json +++ b/packages/react-components/react-file-type-icons/library/project.json @@ -1,8 +1,8 @@ { - "name": "file-type-icons-preview", + "name": "react-file-type-icons", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "projectType": "library", - "sourceRoot": "packages/react-components/file-type-icons-preview/library/src", + "sourceRoot": "packages/react-components/react-file-type-icons/library/src", "tags": ["platform:web", "vNext"], "implicitDependencies": [] } diff --git a/packages/react-components/react-file-type-icons/stories/README.md b/packages/react-components/react-file-type-icons/stories/README.md index ee82402c29b63a..4e07135387f9a6 100644 --- a/packages/react-components/react-file-type-icons/stories/README.md +++ b/packages/react-components/react-file-type-icons/stories/README.md @@ -1,6 +1,6 @@ -# @fluentui/file-type-icons-preview-stories +# @fluentui/react-file-type-icons-stories -Storybook stories for packages/react-components/file-type-icons-preview +Storybook stories for packages/react-components/react-file-type-icons ## Usage @@ -8,7 +8,7 @@ To include within storybook specify stories globs: \`\`\`js module.exports = { -stories: ['../packages/react-components/file-type-icons-preview/stories/src/**/*.mdx', '../packages/react-components/file-type-icons-preview/stories/src/**/index.stories.@(ts|tsx)'], +stories: ['../packages/react-components/react-file-type-icons/stories/src/**/*.mdx', '../packages/react-components/react-file-type-icons/stories/src/**/index.stories.@(ts|tsx)'], } \`\`\` diff --git a/packages/react-components/react-file-type-icons/stories/package.json b/packages/react-components/react-file-type-icons/stories/package.json index a4fbe99940dc2d..75570f7e81e644 100644 --- a/packages/react-components/react-file-type-icons/stories/package.json +++ b/packages/react-components/react-file-type-icons/stories/package.json @@ -1,9 +1,9 @@ { - "name": "@fluentui/file-type-icons-preview-stories", + "name": "@fluentui/react-file-type-icons-stories", "version": "0.0.0", "private": true, "devDependencies": { - "@fluentui/file-type-icons-preview": "*", + "@fluentui/react-file-type-icons": "*", "@fluentui/react-storybook-addon": "*", "@fluentui/react-storybook-addon-export-to-sandbox": "*", "@fluentui/scripts-storybook": "*", diff --git a/packages/react-components/react-file-type-icons/stories/project.json b/packages/react-components/react-file-type-icons/stories/project.json index 8a5f776b3250ea..0a48172868e0f1 100644 --- a/packages/react-components/react-file-type-icons/stories/project.json +++ b/packages/react-components/react-file-type-icons/stories/project.json @@ -1,8 +1,8 @@ { - "name": "file-type-icons-preview-stories", + "name": "react-file-type-icons-stories", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "projectType": "library", - "sourceRoot": "packages/react-components/file-type-icons-preview/stories/src", + "sourceRoot": "packages/react-components/react-file-type-icons/stories/src", "tags": ["vNext", "platform:web", "type:stories"], "implicitDependencies": [] } diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx index 9a60597c678e14..bf32a05e235340 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; import { tokens } from '@fluentui/react-components'; -import { FileTypeIcon, FileIconType } from '@fluentui/file-type-icons-preview'; +import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons'; import { makeStyles, shorthands } from '@griffel/react'; const useStyles = makeStyles({ diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx index 6b6772c7599a05..e473d4f487ee12 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; import { tokens } from '@fluentui/react-components'; -import { FileIconType, FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import { FileIconType, FileTypeIcon } from '@fluentui/react-file-type-icons'; import { makeStyles } from '@griffel/react'; const useStyles = makeStyles({ diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx index e2364404655111..395b0a2fbe1788 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; import { tokens } from '@fluentui/react-components'; -import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import { FileTypeIcon } from '@fluentui/react-file-type-icons'; import { makeStyles, shorthands } from '@griffel/react'; const useStyles = makeStyles({ diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx index 191f098399eeae..35c15390bcc516 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; -import { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from '@fluentui/file-type-icons-preview'; +import { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from '@fluentui/react-file-type-icons'; import { makeStyles, shorthands } from '@griffel/react'; const useStyles = makeStyles({ diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx index 1ab5a0f6625dbe..7b307246cb12a9 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx @@ -1,4 +1,4 @@ -import { FileTypeIcon } from '@fluentui/file-type-icons-preview'; +import { FileTypeIcon } from '@fluentui/react-file-type-icons'; import type { Meta } from '@storybook/react'; import descriptionMd from './FileTypeIconDescription.md'; diff --git a/packages/react-file-type-icons/project.json b/packages/react-file-type-icons/project.json index 49d600378aadde..d22962c6b794da 100644 --- a/packages/react-file-type-icons/project.json +++ b/packages/react-file-type-icons/project.json @@ -1,5 +1,5 @@ { - "name": "react-file-type-icons", + "name": "react-file-type-icons-v8", "$schema": "../../node_modules/nx/schemas/project-schema.json", "projectType": "library", "sourceRoot": "packages/react-file-type-icons/src", diff --git a/tsconfig.base.all.json b/tsconfig.base.all.json index afdc079d0154ba..b37a8ebe8fcc17 100644 --- a/tsconfig.base.all.json +++ b/tsconfig.base.all.json @@ -71,7 +71,7 @@ "@fluentui/eslint-plugin-react-components": [ "packages/react-components/eslint-plugin-react-components/src/index.ts" ], - "@fluentui/file-type-icons-preview": ["packages/react-components/file-type-icons-preview/library/src/index.ts"], + "@fluentui/react-file-type-icons": ["packages/react-components/react-file-type-icons/library/src/index.ts"], "@fluentui/global-context": ["packages/react-components/global-context/src/index.ts"], "@fluentui/keyboard-key": ["packages/keyboard-key/src/index.ts"], "@fluentui/keyboard-keys": ["packages/react-components/keyboard-keys/src/index.ts"], @@ -256,8 +256,8 @@ "@fluentui/visual-regression-assert": ["tools/visual-regression-assert/src/index.ts"], "@fluentui/visual-regression-utilities": ["tools/visual-regression-utilities/src/index.ts"], "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"], - "@fluentui/file-type-icons-preview-stories": [ - "packages/react-components/file-type-icons-preview/stories/src/index.ts" + "@fluentui/react-file-type-icons-stories": [ + "packages/react-components/react-file-type-icons/stories/src/index.ts" ] } } diff --git a/tsconfig.base.json b/tsconfig.base.json index 07e4d6b0dcc2e8..1b053bbdae6acd 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -26,9 +26,9 @@ "@fluentui/eslint-plugin-react-components": [ "packages/react-components/eslint-plugin-react-components/src/index.ts" ], - "@fluentui/file-type-icons-preview": ["packages/react-components/file-type-icons-preview/library/src/index.ts"], - "@fluentui/file-type-icons-preview-stories": [ - "packages/react-components/file-type-icons-preview/stories/src/index.ts" + "@fluentui/react-file-type-icons": ["packages/react-components/react-file-type-icons/library/src/index.ts"], + "@fluentui/react-file-type-icons-stories": [ + "packages/react-components/react-file-type-icons/stories/src/index.ts" ], "@fluentui/global-context": ["packages/react-components/global-context/src/index.ts"], "@fluentui/keyboard-key": ["packages/keyboard-key/src/index.ts"], From f5b16cc1ded6206bf3fcca95fee46da325bf65c0 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 22:37:27 -0800 Subject: [PATCH 32/69] API update and undoing newFileTypeMethod in v8 package --- .../library/etc/react-file-type-icons.api.md | 152 ++++++++++++++++++ .../src/FileIconType.test.ts | 3 +- .../react-file-type-icons/src/FileIconType.ts | 66 +++++--- .../src/FileTypeIconMap.ts | 90 +++-------- .../src/getFileTypeIconProps.ts | 108 ++++++++++--- 5 files changed, 310 insertions(+), 109 deletions(-) create mode 100644 packages/react-components/react-file-type-icons/library/etc/react-file-type-icons.api.md diff --git a/packages/react-components/react-file-type-icons/library/etc/react-file-type-icons.api.md b/packages/react-components/react-file-type-icons/library/etc/react-file-type-icons.api.md new file mode 100644 index 00000000000000..33f598e1176e29 --- /dev/null +++ b/packages/react-components/react-file-type-icons/library/etc/react-file-type-icons.api.md @@ -0,0 +1,152 @@ +## API Report File for "@fluentui/react-file-type-icons" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { ComponentProps } from '@fluentui/react-utilities'; +import type { ComponentState } from '@fluentui/react-utilities'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import type { IIconOptions } from '@fluentui/style-utilities'; +import type { JSXElement } from '@fluentui/react-utilities'; +import * as React_2 from 'react'; +import type { Slot } from '@fluentui/react-utilities'; +import type { SlotClassNames } from '@fluentui/react-utilities'; + +// @public (undocumented) +export const DEFAULT_BASE_URL = "https://res.cdn.office.net/files/fabric-cdn-prod_20251107.003/assets/item-types/"; + +// @public (undocumented) +export const DEFAULT_ICON_SIZE: FileTypeIconSize; + +// @public +export enum FileIconType { + // (undocumented) + album = 21,// Start at 1 so it will evaluate as "truthy" + // (undocumented) + desktopFolder = 9, + // (undocumented) + docset = 1, + // (undocumented) + documentsFolder = 10, + // (undocumented) + folder = 2, + // (undocumented) + form = 14, + // (undocumented) + genericFile = 3, + // (undocumented) + linkedFolder = 12, + // (undocumented) + list = 13, + // (undocumented) + listForm = 22, + // (undocumented) + listItem = 4, + // (undocumented) + loopworkspace = 17, + // (undocumented) + multiple = 6, + // (undocumented) + news = 8, + // (undocumented) + picturesFolder = 11, + // (undocumented) + planner = 18, + // (undocumented) + playlist = 16, + // (undocumented) + portfolio = 20, + // (undocumented) + sharedFolder = 5, + // (undocumented) + stream = 7, + // (undocumented) + sway = 15, + // (undocumented) + todoItem = 19 +} + +// @public +export const FileTypeIcon: ForwardRefComponent; + +// @public (undocumented) +export const fileTypeIconClassNames: SlotClassNames; + +// @public +export const FileTypeIconMap: { + [key: string]: { + extensions?: string[]; + types?: FileIconType[]; + }; +}; + +// @public (undocumented) +export interface FileTypeIconOptions { + extension?: string; + imageFileType?: ImageFileType; + size?: FileTypeIconSize; + type?: FileIconType; +} + +// @public (undocumented) +export type FileTypeIconProps = ComponentProps & { + extension?: string; + type?: FileIconType; + size?: FileTypeIconSize; + imageFileType?: ImageFileType; + baseUrl?: string; +}; + +// @public (undocumented) +export type FileTypeIconSize = 16 | 20 | 24 | 32 | 40 | 48 | 64 | 96; + +// @public (undocumented) +export type FileTypeIconSlots = { + root: Slot<'img'>; +}; + +// @public (undocumented) +export type FileTypeIconState = ComponentState & Required> & { + iconUrl: string; +}; + +// @public +export function getFileTypeIconAsHTMLString(options: FileTypeIconOptions, baseUrl?: string): string | undefined; + +// @public +export function getFileTypeIconAsUrl(options: FileTypeIconOptions, baseUrl?: string): string | undefined; + +// @public +export function getFileTypeIconNameFromExtensionOrType(extension: string | undefined, type: FileIconType | undefined): string; + +// @public +export function getFileTypeIconProps(options: FileTypeIconOptions): { + iconName: string; + 'aria-label'?: string; +}; + +// @public +export function getFileTypeIconSuffix(size: FileTypeIconSize, imageFileType?: ImageFileType, win?: Window): string; + +// @public (undocumented) +export const ICON_SIZES: number[]; + +// @public (undocumented) +export type ImageFileType = 'svg' | 'png'; + +// @public (undocumented) +export function initializeFileTypeIcons(baseUrl?: string, options?: Partial): void; + +// @public +export const renderFileTypeIcon_unstable: (state: FileTypeIconState) => JSXElement; + +// @public +export const useFileTypeIcon_unstable: (props: FileTypeIconProps, ref: React_2.Ref) => FileTypeIconState; + +// @public +export const useFileTypeIconStyles_unstable: (state: FileTypeIconState) => FileTypeIconState; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/react-file-type-icons/src/FileIconType.test.ts b/packages/react-file-type-icons/src/FileIconType.test.ts index 3ca2190759a3dc..47511569bde706 100644 --- a/packages/react-file-type-icons/src/FileIconType.test.ts +++ b/packages/react-file-type-icons/src/FileIconType.test.ts @@ -1,8 +1,9 @@ import { FileIconType } from './FileIconType'; +import type { FileIconTypeInput } from './FileIconType'; let allFileTypeIconValues: FileIconType | undefined; -function validateFileIconTypeValues(allowedFileTypeIconValues: FileIconType | undefined): void { +function validateFileIconTypeValues(allowedFileTypeIconValues: FileIconTypeInput | undefined): void { // The purpose of this function is to verify that the below call compiles, // which may only occur if every enum value matches its key. } diff --git a/packages/react-file-type-icons/src/FileIconType.ts b/packages/react-file-type-icons/src/FileIconType.ts index 38dd670fc363bc..22a7ee22bfcf81 100644 --- a/packages/react-file-type-icons/src/FileIconType.ts +++ b/packages/react-file-type-icons/src/FileIconType.ts @@ -7,25 +7,49 @@ export enum FileIconType { docset = 1, // Start at 1 so it will evaluate as "truthy" - folder, - genericFile, - listItem, - sharedFolder, - multiple, - stream, - news, - desktopFolder, - documentsFolder, - picturesFolder, - linkedFolder, - list, - form, - sway, - playlist, - loopworkspace, - planner, - todoItem, - portfolio, - album, - listForm, + folder = 2, + genericFile = 3, + listItem = 4, + sharedFolder = 5, + multiple = 6, + stream = 7, + news = 8, + desktopFolder = 9, + documentsFolder = 10, + picturesFolder = 11, + linkedFolder = 12, + list = 13, + form = 14, + sway = 15, + playlist = 16, + loopworkspace = 17, + planner = 18, + todoItem = 19, + portfolio = 20, + album = 21, + listForm = 22, } + +export type FileIconTypeInput = + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17 + | 18 + | 19 + | 20 + | 21 + | 22; diff --git a/packages/react-file-type-icons/src/FileTypeIconMap.ts b/packages/react-file-type-icons/src/FileTypeIconMap.ts index 01d5916909fb23..a324f893e1fbf3 100644 --- a/packages/react-file-type-icons/src/FileTypeIconMap.ts +++ b/packages/react-file-type-icons/src/FileTypeIconMap.ts @@ -1,11 +1,9 @@ -import { FileIconType } from './FileIconType'; - /** * Enumeration of icon file names, and what extensions they map to. * Please keep items alphabetical. Items without extensions may require specific logic in the code to map. * Always use getFileTypeIconProps to get the most up-to-date icon at the right pixel density. */ -export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: FileIconType[] } } = { +export const FileTypeIconMap: { [key: string]: { extensions?: string[] } } = { accdb: { extensions: ['accdb', 'mdb'], }, @@ -15,9 +13,7 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: archive: { extensions: ['7z', 'ace', 'arc', 'arj', 'dmg', 'gz', 'iso', 'lzh', 'pkg', 'rar', 'sit', 'tgz', 'tar', 'z'], }, - album: { - types: [FileIconType.album], - }, + album: {}, audio: { extensions: [ 'aif', @@ -279,15 +275,9 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: designer: { extensions: ['design'], }, - desktopfolder: { - types: [FileIconType.desktopFolder], - }, - docset: { - types: [FileIconType.docset], - }, - documentsfolder: { - types: [FileIconType.documentsFolder], - }, + desktopfolder: {}, + docset: {}, + documentsfolder: {}, docx: { extensions: ['doc', 'docm', 'docx', 'docb'], }, @@ -300,18 +290,13 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: exe: { extensions: ['application', 'appref-ms', 'apk', 'app', 'appx', 'exe', 'ipa', 'msi', 'xap'], }, - folder: { - types: [FileIconType.folder], - }, + favoritesfolder: {}, + folder: {}, font: { extensions: ['ttf', 'otf', 'woff'], }, - form: { - types: [FileIconType.form], - }, - genericfile: { - types: [FileIconType.genericFile], - }, + form: {}, + genericfile: {}, html: { extensions: ['htm', 'html', 'mht', 'mhtml'], }, @@ -321,27 +306,17 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: link: { extensions: ['lnk', 'link', 'url', 'website', 'webloc'], }, - linkedfolder: { - types: [FileIconType.linkedFolder], - }, - listform: { - types: [FileIconType.listForm], - }, - listitem: { - types: [FileIconType.listItem], - }, + linkedfolder: {}, + listform: {}, + listitem: {}, loop: { extensions: ['fluid', 'loop', 'note'], }, - loopworkspace: { - types: [FileIconType.loopworkspace], - }, + loopworkspace: {}, officescript: { extensions: ['osts'], }, - splist: { - types: [FileIconType.list], - }, + splist: {}, mcworld: { extensions: ['mcworld'], }, @@ -384,9 +359,7 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: mpt: { extensions: ['mpt'], }, - multiple: { - types: [FileIconType.multiple], - }, + multiple: {}, one: { // This is a partial OneNote page or section export. Not whole notebooks, see "onetoc" extensions: ['one', 'onepart'], @@ -446,15 +419,9 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: ], }, photo360: {}, - picturesfolder: { - types: [FileIconType.picturesFolder], - }, - planner: { - types: [FileIconType.planner], - }, - portfolio: { - types: [FileIconType.portfolio], - }, + picturesfolder: {}, + planner: {}, + portfolio: {}, potx: { extensions: ['pot', 'potm', 'potx'], }, @@ -476,24 +443,16 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: spo: { extensions: ['aspx'], }, - sponews: { - types: [FileIconType.news], - }, + sponews: {}, spreadsheet: { extensions: ['odc', 'ods', 'gsheet', 'numbers', 'tsv'], }, rtf: { extensions: ['epub', 'gdoc', 'odt', 'rtf', 'wri', 'pages'], }, - sharedfolder: { - types: [FileIconType.sharedFolder], - }, - playlist: { - types: [FileIconType.playlist], - }, - sway: { - types: [FileIconType.sway], - }, + sharedfolder: {}, + playlist: {}, + sway: {}, sysfile: { extensions: [ 'bak', @@ -527,9 +486,7 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: 'xll', ], }, - todoitem: { - types: [FileIconType.todoItem], - }, + todoitem: {}, txt: { extensions: ['dif', 'diff', 'readme', 'out', 'plist', 'properties', 'text', 'txt'], }, @@ -562,7 +519,6 @@ export const FileTypeIconMap: { [key: string]: { extensions?: string[]; types?: ], }, video: { - types: [FileIconType.stream], extensions: [ '3g2', '3gp', diff --git a/packages/react-file-type-icons/src/getFileTypeIconProps.ts b/packages/react-file-type-icons/src/getFileTypeIconProps.ts index e473167d7c9fbb..f0a13076e346ef 100644 --- a/packages/react-file-type-icons/src/getFileTypeIconProps.ts +++ b/packages/react-file-type-icons/src/getFileTypeIconProps.ts @@ -1,10 +1,31 @@ import { FileTypeIconMap } from './FileTypeIconMap'; import { FileIconType } from './FileIconType'; +import type { FileIconTypeInput } from './FileIconType'; let _extensionToIconName: { [key: string]: string }; -let _typeToIconName: { [key: number]: string }; const GENERIC_FILE = 'genericfile'; +const FOLDER = 'folder'; +const SHARED_FOLDER = 'sharedfolder'; +const DOCSET_FOLDER = 'docset'; +const LIST_ITEM = 'listitem'; +const LIST = 'splist'; +const MULTIPLE_ITEMS = 'multiple'; +const NEWS = 'sponews'; +const STREAM = 'video'; +const DESKTOP_FOLDER = 'desktopfolder'; +const DOCUMENTS_FOLDER = 'documentsfolder'; +const PICTURES_FOLDER = 'picturesfolder'; +const LINKED_FOLDER = 'linkedfolder'; +const FORM = 'form'; +const SWAY = 'sway'; +const PLAYLIST = 'playlist'; +const LOOP_WORKSPACE = 'loopworkspace'; +const TODOITEM = 'todoitem'; +const PLANNER = 'planner'; +const PORTFOLIO = 'portfolio'; +const ALBUM = 'album'; +const LIST_FORM = 'listform'; export const DEFAULT_ICON_SIZE: FileTypeIconSize = 16; export type FileTypeIconSize = 16 | 20 | 24 | 32 | 40 | 48 | 64 | 96; @@ -22,7 +43,7 @@ export interface IFileTypeIconOptions { * file type icons that are not associated with a file extension, * such as folder. */ - type?: FileIconType; + type?: FileIconTypeInput; /** * The size of the icon in pixels. * @default 16 @@ -82,26 +103,73 @@ export function getFileTypeIconNameFromExtensionOrType( extension = extension.replace('.', '').toLowerCase(); return _extensionToIconName[extension] || GENERIC_FILE; } else if (type) { - if (!_typeToIconName) { - _typeToIconName = {}; - - for (const iconName in FileTypeIconMap) { - if (FileTypeIconMap.hasOwnProperty(iconName)) { - const types = FileTypeIconMap[iconName].types; - - if (types) { - for (let i = 0; i < types.length; i++) { - _typeToIconName[types[i]] = iconName; - } - } - } - } + switch (type) { + case FileIconType.docset: + iconBaseName = DOCSET_FOLDER; + break; + case FileIconType.folder: + iconBaseName = FOLDER; + break; + case FileIconType.listItem: + iconBaseName = LIST_ITEM; + break; + case FileIconType.sharedFolder: + iconBaseName = SHARED_FOLDER; + break; + case FileIconType.stream: + iconBaseName = STREAM; + break; + case FileIconType.multiple: + iconBaseName = MULTIPLE_ITEMS; + break; + case FileIconType.news: + iconBaseName = NEWS; + break; + case FileIconType.desktopFolder: + iconBaseName = DESKTOP_FOLDER; + break; + case FileIconType.documentsFolder: + iconBaseName = DOCUMENTS_FOLDER; + break; + case FileIconType.picturesFolder: + iconBaseName = PICTURES_FOLDER; + break; + case FileIconType.linkedFolder: + iconBaseName = LINKED_FOLDER; + break; + case FileIconType.list: + iconBaseName = LIST; + break; + case FileIconType.form: + iconBaseName = FORM; + break; + case FileIconType.sway: + iconBaseName = SWAY; + break; + case FileIconType.playlist: + iconBaseName = PLAYLIST; + break; + case FileIconType.loopworkspace: + iconBaseName = LOOP_WORKSPACE; + break; + case FileIconType.planner: + iconBaseName = PLANNER; + break; + case FileIconType.todoItem: + iconBaseName = TODOITEM; + break; + case FileIconType.portfolio: + iconBaseName = PORTFOLIO; + break; + case FileIconType.album: + iconBaseName = ALBUM; + break; + case FileIconType.listForm: + iconBaseName = LIST_FORM; + break; } - - return _typeToIconName[type] || GENERIC_FILE; } - - return GENERIC_FILE; + return iconBaseName || GENERIC_FILE; } export function getFileTypeIconSuffix( From baa34305b0c05a5c3d566e711e927a587902919e Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Mon, 24 Nov 2025 23:41:37 -0800 Subject: [PATCH 33/69] package semver cleanup, build pass, documentation simplification --- .../react-components/package.json | 2 +- .../library/package.json | 4 +- .../library/src/FileTypeIcon.ts | 6 +-- .../FileTypeIcon/useFileTypeIcon.ts | 8 +--- .../library/src/index.ts | 6 +-- .../src/utils/getFileTypeIconAsUrl.test.ts | 5 +- .../library/src/utils/getFileTypeIconProps.ts | 4 +- .../FileTypeIconDefault.stories.tsx | 17 +++++-- .../FileTypeIconEdgeCases.stories.tsx | 47 ++++--------------- tsconfig.base.all.json | 1 - tsconfig.base.json | 8 ++-- 11 files changed, 34 insertions(+), 74 deletions(-) diff --git a/packages/react-components/react-components/package.json b/packages/react-components/react-components/package.json index 5a563bee9b8ce3..cb8502b2543773 100644 --- a/packages/react-components/react-components/package.json +++ b/packages/react-components/react-components/package.json @@ -78,7 +78,7 @@ "@fluentui/react-carousel": "^9.8.12", "@fluentui/react-color-picker": "^9.2.11", "@fluentui/react-nav": "^9.3.14", - "@fluentui/react-file-type-icons": "^0.0.1" + "@fluentui/react-file-type-icons": "^9.0.0" }, "peerDependencies": { "@types/react": ">=16.14.0 <20.0.0", diff --git a/packages/react-components/react-file-type-icons/library/package.json b/packages/react-components/react-file-type-icons/library/package.json index b339ac524adfae..99660817316e34 100644 --- a/packages/react-components/react-file-type-icons/library/package.json +++ b/packages/react-components/react-file-type-icons/library/package.json @@ -1,8 +1,8 @@ { "name": "@fluentui/react-file-type-icons", - "version": "0.0.0", + "version": "9.0.0", "private": true, - "description": "New fluentui react package", + "description": "Filetype icons for FluentUI", "main": "lib-commonjs/index.js", "module": "lib/index.js", "typings": "./dist/index.d.ts", diff --git a/packages/react-components/react-file-type-icons/library/src/FileTypeIcon.ts b/packages/react-components/react-file-type-icons/library/src/FileTypeIcon.ts index 38044ff59d9603..a24037c4ce477b 100644 --- a/packages/react-components/react-file-type-icons/library/src/FileTypeIcon.ts +++ b/packages/react-components/react-file-type-icons/library/src/FileTypeIcon.ts @@ -1,8 +1,4 @@ -export type { - FileTypeIconProps, - FileTypeIconSlots, - FileTypeIconState, -} from './components/FileTypeIcon/index'; +export type { FileTypeIconProps, FileTypeIconSlots, FileTypeIconState } from './components/FileTypeIcon/index'; export { FileTypeIcon, fileTypeIconClassNames, diff --git a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts index 9be1f7105007b4..916fa7c18e8ddd 100644 --- a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts +++ b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts @@ -17,13 +17,7 @@ export const useFileTypeIcon_unstable = ( props: FileTypeIconProps, ref: React.Ref, ): FileTypeIconState => { - const { - extension, - type, - size = DEFAULT_ICON_SIZE, - imageFileType = 'svg', - baseUrl = DEFAULT_BASE_URL, - } = props; + const { extension, type, size = DEFAULT_ICON_SIZE, imageFileType = 'svg', baseUrl = DEFAULT_BASE_URL } = props; // Get the base icon name and suffix separately using v8 pattern const baseIconName = getFileTypeIconNameFromExtensionOrType(extension, type); diff --git a/packages/react-components/react-file-type-icons/library/src/index.ts b/packages/react-components/react-file-type-icons/library/src/index.ts index 303a3719d4577f..03e642d227d94b 100644 --- a/packages/react-components/react-file-type-icons/library/src/index.ts +++ b/packages/react-components/react-file-type-icons/library/src/index.ts @@ -17,10 +17,6 @@ export { getFileTypeIconSuffix, DEFAULT_ICON_SIZE, } from './utils/getFileTypeIconProps'; -export type { - FileTypeIconSize, - ImageFileType, - FileTypeIconOptions, -} from './utils/getFileTypeIconProps'; +export type { FileTypeIconSize, ImageFileType, FileTypeIconOptions } from './utils/getFileTypeIconProps'; export { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from './utils/getFileTypeIconAsUrl'; export { initializeFileTypeIcons, DEFAULT_BASE_URL, ICON_SIZES } from './utils/initializeFileTypeIcons'; diff --git a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts index b543a33f118167..06c332ebfcc05a 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts @@ -297,10 +297,7 @@ describe('getFileTypeIconAsHTMLString', () => { const customBaseUrl = 'https://custom.cdn.com/icons/'; it('should use custom base URL in HTML string', () => { - const result = getFileTypeIconAsHTMLString( - { extension: 'docx', size: 16, imageFileType: 'svg' }, - customBaseUrl, - ); + const result = getFileTypeIconAsHTMLString({ extension: 'docx', size: 16, imageFileType: 'svg' }, customBaseUrl); expect(result).toBe(``); }); }); diff --git a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts index 879a1cbad7233c..d45d02b8e01362 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts @@ -86,7 +86,6 @@ export function getFileTypeIconNameFromExtensionOrType( // Strip periods, force lowercase. extension = extension.replace('.', '').toLowerCase(); return _extensionToIconName[extension] || GENERIC_FILE; - } else if (type) { if (!_typeToIconName) { _typeToIconName = {}; @@ -131,11 +130,10 @@ export function getFileTypeIconSuffix( if (imageFileType === 'svg' && devicePixelRatio > 1 && devicePixelRatio <= 1.9) { // 1.5x is a special case where SVGs need a different image. devicePixelRatioSuffix = '_1.5x'; - } else if (imageFileType === 'png') { // To look good, PNGs should use a different image for higher device pixel ratios if (devicePixelRatio > 1 && devicePixelRatio <= 1.5) { - devicePixelRatioSuffix ='_1.5x'; + devicePixelRatioSuffix = '_1.5x'; } else if (devicePixelRatio > 1.5 && devicePixelRatio <= 2) { devicePixelRatioSuffix = '_2x'; } else if (devicePixelRatio > 2 && devicePixelRatio <= 3) { diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx index e473d4f487ee12..506926f859b70a 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx @@ -35,12 +35,16 @@ export const Default = (): JSXElement => {
16px
-
Compact UIs, toolbars, dense tables. Example shows a Word document filetype icon.
+
+ Compact UIs, toolbars, dense tables. Example shows a Word document filetype icon. +
20px
-
Compact lists, navigation items. Example shows a folder, referenced via FileIconType.
+
+ Compact lists, navigation items. Example shows a folder, referenced via FileIconType. +
@@ -50,7 +54,9 @@ export const Default = (): JSXElement => {
32px
-
Standard cards, file browsers. Example shows a PowerPoint presentation with a legacy file extension.
+
+ Standard cards, file browsers. Example shows a PowerPoint presentation with a legacy file extension. +
@@ -60,7 +66,9 @@ export const Default = (): JSXElement => {
48px
-
Grid views, attachment previews. Example shows a Microsoft Lists object.
+
+ Grid views, attachment previews. Example shows a Microsoft Lists object. +
@@ -84,4 +92,3 @@ Default.parameters = { }, }, }; - diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx index 395b0a2fbe1788..4ded35267789be 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx @@ -190,10 +190,10 @@ export const EdgeCases = (): JSXElement => {
-
Case Sensitivity
+
Case Sensitivity and Periods
File extensions are handled in a case-insensitive manner. The component recognizes extensions regardless of - capitalization. + capitalization. Periods before the extension will be ignored.
@@ -205,24 +205,16 @@ export const EdgeCases = (): JSXElement => {
.pdf
- +
.Pdf
-
- -
.PdF
-
-
All variations display the same PDF icon
-
- -
-
Best Practice: Handle Edge Cases in Your Code
-
- When integrating FileTypeIcon, consider extracting and normalizing file extensions from full filenames: +
All variations display the same PDF icon. If you want to further sanitize + your code, consider extracting and normalizing file extensions from full filenames:
- - {`// Extract extension from filename +
+          
+            {`// Extract extension from filename
 const getExtension = (filename: string): string => {
   const parts = filename.split('.');
   return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : '';
@@ -233,29 +225,10 @@ const filename = "Report.Final.PDF";
 const extension = getExtension(filename); // "pdf"
 
 `}
-        
+          
+        
-
-
Extensions with Leading Dots
-
- The extension prop should be provided without the leading dot. If a dot is included, the component handles it - appropriately. -
-
-
- -
extension="docx" ✓
-
-
- -
extension=".docx"
-
-
-
- Best Practice: Always provide extensions without the leading dot for consistent behavior. -
-
); }; diff --git a/tsconfig.base.all.json b/tsconfig.base.all.json index b37a8ebe8fcc17..fa01a90c54fe9b 100644 --- a/tsconfig.base.all.json +++ b/tsconfig.base.all.json @@ -26,7 +26,6 @@ "@fluentui/react-date-time": ["packages/react-date-time/src/index.ts"], "@fluentui/react-experiments": ["packages/react-experiments/src/index.ts"], "@fluentui/react-experiments/lib/*": ["packages/react-experiments/src/*"], - "@fluentui/react-file-type-icons": ["packages/react-file-type-icons/src/index.ts"], "@fluentui/react-focus": ["packages/react-focus/src/index.ts"], "@fluentui/react-hooks": ["packages/react-hooks/src/index.ts"], "@fluentui/scheme-utilities": ["packages/scheme-utilities/src/index.ts"], diff --git a/tsconfig.base.json b/tsconfig.base.json index 1b053bbdae6acd..77fee2fa2b0840 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -26,10 +26,6 @@ "@fluentui/eslint-plugin-react-components": [ "packages/react-components/eslint-plugin-react-components/src/index.ts" ], - "@fluentui/react-file-type-icons": ["packages/react-components/react-file-type-icons/library/src/index.ts"], - "@fluentui/react-file-type-icons-stories": [ - "packages/react-components/react-file-type-icons/stories/src/index.ts" - ], "@fluentui/global-context": ["packages/react-components/global-context/src/index.ts"], "@fluentui/keyboard-key": ["packages/keyboard-key/src/index.ts"], "@fluentui/keyboard-keys": ["packages/react-components/keyboard-keys/src/index.ts"], @@ -80,6 +76,10 @@ "@fluentui/react-drawer-stories": ["packages/react-components/react-drawer/stories/src/index.ts"], "@fluentui/react-field": ["packages/react-components/react-field/library/src/index.ts"], "@fluentui/react-field-stories": ["packages/react-components/react-field/stories/src/index.ts"], + "@fluentui/react-file-type-icons": ["packages/react-components/react-file-type-icons/library/src/index.ts"], + "@fluentui/react-file-type-icons-stories": [ + "packages/react-components/react-file-type-icons/stories/src/index.ts" + ], "@fluentui/react-focus-management": ["packages/react-focus-management/src/index.ts"], "@fluentui/react-icons-compat": ["packages/react-components/react-icons-compat/library/src/index.ts"], "@fluentui/react-icons-compat-stories": ["packages/react-components/react-icons-compat/stories/src/index.ts"], From 1f4861249b882f0269371324cfb4303965d28065 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 00:20:12 -0800 Subject: [PATCH 34/69] error checking and optimizations --- .../FileTypeIcon/useFileTypeIcon.ts | 3 +- .../src/utils/getFileTypeIconAsUrl.test.ts | 32 ++++++++++++----- .../library/src/utils/getFileTypeIconAsUrl.ts | 7 ++-- .../library/src/utils/getFileTypeIconProps.ts | 2 +- .../FileTypeIconCommon.stories.tsx | 8 +++-- .../FileTypeIconDefault.stories.tsx | 2 +- .../FileTypeIconEdgeCases.stories.tsx | 12 +++++-- .../FileTypeIconUrlAndHtml.stories.tsx | 35 ++++++++++--------- .../src/FileTypeIcon/index.stories.tsx | 2 +- 9 files changed, 65 insertions(+), 38 deletions(-) diff --git a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts index 916fa7c18e8ddd..a788a518a4083e 100644 --- a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts +++ b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts @@ -7,8 +7,7 @@ import { DEFAULT_ICON_SIZE, } from '../../utils/getFileTypeIconProps'; import { FileIconType } from '../../utils/FileIconType'; - -const DEFAULT_BASE_URL = 'https://res.cdn.office.net/files/fabric-cdn-prod_20251119.001/assets/item-types/'; +import { DEFAULT_BASE_URL } from '../../utils/initializeFileTypeIcons'; /** * Returns the props and state required to render the FileTypeIcon component. diff --git a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts index 06c332ebfcc05a..f53ee55e9ca51c 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts @@ -217,7 +217,7 @@ describe('getFileTypeIconAsHTMLString', () => { { extension: 'docx', size: 16, imageFileType: 'png' }, DEFAULT_BASE_URL, ); - expect(result).toBe(``); + expect(result).toBe(`docx file icon`); }); it('should return correct HTML string for 1.5x DPI with pdf extension', () => { @@ -231,7 +231,7 @@ describe('getFileTypeIconAsHTMLString', () => { { extension: 'pdf', size: 16, imageFileType: 'png' }, DEFAULT_BASE_URL, ); - expect(result).toBe(``); + expect(result).toBe(`pdf file icon`); }); it('should return correct HTML string for 2x DPI with xlsx extension', () => { @@ -245,7 +245,7 @@ describe('getFileTypeIconAsHTMLString', () => { { extension: 'xlsx', size: 16, imageFileType: 'png' }, DEFAULT_BASE_URL, ); - expect(result).toBe(``); + expect(result).toBe(`xlsx file icon`); }); }); @@ -255,7 +255,7 @@ describe('getFileTypeIconAsHTMLString', () => { { extension: 'docx', size: 24, imageFileType: 'svg' }, DEFAULT_BASE_URL, ); - expect(result).toBe(``); + expect(result).toBe(`docx file icon`); }); it('should return correct HTML string for 1.5x DPI with SVG', () => { @@ -269,7 +269,7 @@ describe('getFileTypeIconAsHTMLString', () => { { extension: 'pdf', size: 20, imageFileType: 'svg' }, DEFAULT_BASE_URL, ); - expect(result).toBe(``); + expect(result).toBe(`pdf file icon`); }); }); @@ -281,6 +281,7 @@ describe('getFileTypeIconAsHTMLString', () => { ); expect(result).toContain('height="32"'); expect(result).toContain('width="32"'); + expect(result).toContain('alt="docx file icon"'); }); it('should include correct size attributes for size 48', () => { @@ -290,6 +291,7 @@ describe('getFileTypeIconAsHTMLString', () => { ); expect(result).toContain('height="48"'); expect(result).toContain('width="48"'); + expect(result).toContain('alt="xlsx file icon"'); }); }); @@ -298,16 +300,28 @@ describe('getFileTypeIconAsHTMLString', () => { it('should use custom base URL in HTML string', () => { const result = getFileTypeIconAsHTMLString({ extension: 'docx', size: 16, imageFileType: 'svg' }, customBaseUrl); - expect(result).toBe(``); + expect(result).toBe(`docx file icon`); }); }); describe('edge cases', () => { - it('should handle undefined URL gracefully', () => { - // This would only happen if getFileTypeIconAsUrl returns undefined - // which shouldn't happen in normal circumstances, but we test for robustness + it('should handle unknown extension gracefully with alt text', () => { + // Unknown extensions should still generate valid HTML with alt text const result = getFileTypeIconAsHTMLString({ extension: 'unknown', size: 16 }, DEFAULT_BASE_URL); expect(result).toBeDefined(); + expect(result).toContain('alt="unknown file icon"'); + }); + + it('should generate alt text from FileIconType when extension is not provided', () => { + const result = getFileTypeIconAsHTMLString({ type: FileIconType.folder, size: 16 }, DEFAULT_BASE_URL); + expect(result).toBeDefined(); + expect(result).toContain('alt="folder file icon"'); + }); + + it('should generate empty alt text prefix when neither extension nor type is provided', () => { + const result = getFileTypeIconAsHTMLString({ size: 16 }, DEFAULT_BASE_URL); + expect(result).toBeDefined(); + expect(result).toContain('alt=" file icon"'); }); }); }); diff --git a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts index c341e892ced94a..e7bc5d77d5c63a 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts @@ -5,6 +5,7 @@ import { DEFAULT_ICON_SIZE, } from './getFileTypeIconProps'; import type { FileTypeIconOptions } from './getFileTypeIconProps'; +import { FileIconType } from './FileIconType'; /** * Given the `fileTypeIconOptions`, this function returns the CDN-based URL for `FileTypeIcon`. @@ -47,6 +48,8 @@ export function getFileTypeIconAsHTMLString( return undefined; } - const { size = DEFAULT_ICON_SIZE } = options; - return ``; + const { size = DEFAULT_ICON_SIZE, extension, type } = options; + // Generate alt text: use extension if provided, otherwise get the enum name for type + const altText = extension || (type !== undefined ? FileIconType[type] : ''); + return `${altText} file icon`; } diff --git a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts index d45d02b8e01362..780fe314408d7e 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts @@ -122,7 +122,7 @@ export function getFileTypeIconSuffix( win?: Window, ): string { // eslint-disable-next-line @nx/workspace-no-restricted-globals - win ??= window; + win ??= typeof window !== 'undefined' ? window : ({ devicePixelRatio: 1 } as Window); const devicePixelRatio: number = win.devicePixelRatio; let devicePixelRatioSuffix = ''; // Default is 1x diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx index bf32a05e235340..4449b2c8568af6 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx @@ -42,13 +42,17 @@ const useStyles = makeStyles({ textAlign: 'center', color: tokens.colorNeutralForeground2, }, + specialTypesDescription: { + marginBottom: '12px', + color: tokens.colorNeutralForeground3, + }, }); const fileTypeCategories = { documents: ['docx', 'xlsx', 'pdf', 'txt', 'rtf', 'odt', 'pptx', 'csv'], media: ['jpg', 'svg', 'mp4', 'mp3', 'wav', 'aac'], code: ['html', 'url', 'json', 'xml', 'py', 'java', 'cpp'], - data: ['zip', 'tar', 'sql', 'accdb', 'xml'], + data: ['zip', 'tar', 'sql', 'accdb'], }; const specialTypes = [ @@ -114,7 +118,7 @@ export const CommonFileTypes = (): JSXElement => {
Special Types
-

+

These types are not based on file extensions, but represent objects like folders and list items.

diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx index 506926f859b70a..db8cda511a7f51 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx @@ -49,7 +49,7 @@ export const Default = (): JSXElement => {
24px
-
Standard list items, search results. Example shows a PDF.
+
Standard list items, search results. Example shows a Word document.
diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx index 4ded35267789be..35fc6816866298 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx @@ -62,6 +62,12 @@ const useStyles = makeStyles({ fontSize: tokens.fontSizeBase200, overflowX: 'auto', }, + truncatedText: { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + flex: 1, + }, }); export const EdgeCases = (): JSXElement => { @@ -177,7 +183,7 @@ export const EdgeCases = (): JSXElement => {
-
+
This_is_a_very_long_filename_that_could_cause_layout_issues_in_some_contexts_Q4_2025_Final_Report_v3.pdf
@@ -198,11 +204,11 @@ export const EdgeCases = (): JSXElement => {
-
.PDF
+
PDF
-
.pdf
+
pdf
diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx index 35c15390bcc516..64f5bd35d3294b 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; +import { tokens } from '@fluentui/react-components'; import { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from '@fluentui/react-file-type-icons'; import { makeStyles, shorthands } from '@griffel/react'; @@ -14,12 +15,12 @@ const useStyles = makeStyles({ flexDirection: 'column', gap: '12px', ...shorthands.padding('16px'), - ...shorthands.border('1px', 'solid', '#e0e0e0'), - ...shorthands.borderRadius('8px'), + ...shorthands.border('1px', 'solid', tokens.colorNeutralStroke1), + ...shorthands.borderRadius(tokens.borderRadiusMedium), }, sectionTitle: { - fontSize: '16px', - fontWeight: '600', + fontSize: tokens.fontSizeBase400, + fontWeight: tokens.fontWeightSemibold, marginBottom: '8px', }, grid: { @@ -32,12 +33,12 @@ const useStyles = makeStyles({ flexDirection: 'column', gap: '8px', ...shorthands.padding('12px'), - backgroundColor: '#f5f5f5', - ...shorthands.borderRadius('4px'), + backgroundColor: tokens.colorNeutralBackground2, + ...shorthands.borderRadius(tokens.borderRadiusSmall), }, cardTitle: { - fontSize: '14px', - fontWeight: '600', + fontSize: tokens.fontSizeBase300, + fontWeight: tokens.fontWeightSemibold, marginBottom: '4px', }, iconPreview: { @@ -45,23 +46,23 @@ const useStyles = makeStyles({ alignItems: 'center', justifyContent: 'center', ...shorthands.padding('12px'), - backgroundColor: '#ffffff', - ...shorthands.borderRadius('4px'), + backgroundColor: tokens.colorNeutralBackground1, + ...shorthands.borderRadius(tokens.borderRadiusSmall), minHeight: '60px', }, code: { - fontSize: '11px', - fontFamily: 'monospace', - backgroundColor: '#ffffff', + fontSize: tokens.fontSizeBase100, + fontFamily: tokens.fontFamilyMonospace, + backgroundColor: tokens.colorNeutralBackground1, ...shorthands.padding('8px'), - ...shorthands.borderRadius('4px'), + ...shorthands.borderRadius(tokens.borderRadiusSmall), overflowX: 'auto', wordBreak: 'break-all', }, label: { - fontSize: '12px', - color: '#666', - fontWeight: '500', + fontSize: tokens.fontSizeBase200, + color: tokens.colorNeutralForeground3, + fontWeight: tokens.fontWeightMedium, }, }); diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx index 7b307246cb12a9..06451131e0a04d 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx @@ -26,7 +26,7 @@ export default { description: 'The size of the icon in pixels', table: { type: { summary: '16 | 20 | 24 | 32 | 40 | 48 | 64 | 96' }, - defaultValue: { summary: '48' }, + defaultValue: { summary: '16' }, }, }, extension: { From 5996d700eb386139e535315fee46a06c4f9d8b30 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 00:31:48 -0800 Subject: [PATCH 35/69] initializeFileTypeIcons with alternate CDN URL is now honored by new methods --- .../FileTypeIcon/FileTypeIcon.test.tsx | 49 ++++++ .../FileTypeIcon/useFileTypeIcon.ts | 4 +- .../library/src/index.ts | 8 +- .../library/src/utils/getFileTypeIconAsUrl.ts | 14 +- .../src/utils/initializeFileTypeIcons.test.ts | 149 ++++++++++++++++++ .../src/utils/initializeFileTypeIcons.tsx | 23 +++ 6 files changed, 239 insertions(+), 8 deletions(-) create mode 100644 packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.test.ts diff --git a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx index bd3a677bf1656c..f4b2df95e985e0 100644 --- a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx +++ b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx @@ -3,6 +3,11 @@ import { isConformant } from '../../testing/isConformant'; import { render } from '@testing-library/react'; import { FileTypeIcon } from './FileTypeIcon'; import { FileIconType } from '../../utils/FileIconType'; +import { + initializeFileTypeIcons, + resetConfiguredBaseUrl, + DEFAULT_BASE_URL, +} from '../../utils/initializeFileTypeIcons'; describe('FileTypeIcon', () => { isConformant({ @@ -72,3 +77,47 @@ describe('FileTypeIcon', () => { expect(img?.src).toContain('genericfile'); }); }); + +describe('FileTypeIcon with initializeFileTypeIcons', () => { + beforeEach(() => { + // Reset to default state before each test + resetConfiguredBaseUrl(); + }); + + afterAll(() => { + // Reset to default state after all tests + resetConfiguredBaseUrl(); + }); + + it('should use DEFAULT_BASE_URL when initializeFileTypeIcons has not been called', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img?.src).toContain(DEFAULT_BASE_URL); + }); + + it('should use custom URL after initializeFileTypeIcons is called with custom URL', () => { + const customUrl = 'https://my-custom-cdn.com/icons/'; + initializeFileTypeIcons(customUrl); + + const result = render(); + const img = result.container.querySelector('img'); + expect(img?.src).toContain(customUrl); + }); + + it('should allow explicit baseUrl prop to override configured URL', () => { + const configuredUrl = 'https://configured-cdn.com/icons/'; + const explicitUrl = 'https://explicit-cdn.com/icons/'; + + initializeFileTypeIcons(configuredUrl); + + // Without explicit baseUrl - uses configured URL + const result1 = render(); + const img1 = result1.container.querySelector('img'); + expect(img1?.src).toContain(configuredUrl); + + // With explicit baseUrl - uses explicit URL + const result2 = render(); + const img2 = result2.container.querySelector('img'); + expect(img2?.src).toContain(explicitUrl); + }); +}); diff --git a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts index a788a518a4083e..db9181477c03c6 100644 --- a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts +++ b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts @@ -7,7 +7,7 @@ import { DEFAULT_ICON_SIZE, } from '../../utils/getFileTypeIconProps'; import { FileIconType } from '../../utils/FileIconType'; -import { DEFAULT_BASE_URL } from '../../utils/initializeFileTypeIcons'; +import { getConfiguredBaseUrl } from '../../utils/initializeFileTypeIcons'; /** * Returns the props and state required to render the FileTypeIcon component. @@ -16,7 +16,7 @@ export const useFileTypeIcon_unstable = ( props: FileTypeIconProps, ref: React.Ref, ): FileTypeIconState => { - const { extension, type, size = DEFAULT_ICON_SIZE, imageFileType = 'svg', baseUrl = DEFAULT_BASE_URL } = props; + const { extension, type, size = DEFAULT_ICON_SIZE, imageFileType = 'svg', baseUrl = getConfiguredBaseUrl() } = props; // Get the base icon name and suffix separately using v8 pattern const baseIconName = getFileTypeIconNameFromExtensionOrType(extension, type); diff --git a/packages/react-components/react-file-type-icons/library/src/index.ts b/packages/react-components/react-file-type-icons/library/src/index.ts index 03e642d227d94b..f8f5771663adc9 100644 --- a/packages/react-components/react-file-type-icons/library/src/index.ts +++ b/packages/react-components/react-file-type-icons/library/src/index.ts @@ -19,4 +19,10 @@ export { } from './utils/getFileTypeIconProps'; export type { FileTypeIconSize, ImageFileType, FileTypeIconOptions } from './utils/getFileTypeIconProps'; export { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from './utils/getFileTypeIconAsUrl'; -export { initializeFileTypeIcons, DEFAULT_BASE_URL, ICON_SIZES } from './utils/initializeFileTypeIcons'; +export { + initializeFileTypeIcons, + getConfiguredBaseUrl, + resetConfiguredBaseUrl, + DEFAULT_BASE_URL, + ICON_SIZES, +} from './utils/initializeFileTypeIcons'; diff --git a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts index e7bc5d77d5c63a..88ee15e35535bc 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts @@ -1,4 +1,4 @@ -import { DEFAULT_BASE_URL } from './initializeFileTypeIcons'; +import { getConfiguredBaseUrl } from './initializeFileTypeIcons'; import { getFileTypeIconNameFromExtensionOrType, getFileTypeIconSuffix, @@ -11,11 +11,13 @@ import { FileIconType } from './FileIconType'; * Given the `fileTypeIconOptions`, this function returns the CDN-based URL for `FileTypeIcon`. * Similar to `getFileTypeIconProps`, but rather than returning the `iconName`, this returns the raw URL. * @param options - * @param baseUrl - optionally provide a custom CDN base url to fetch icons from + * @param baseUrl - optionally provide a custom CDN base url to fetch icons from. + * If not provided, uses the URL configured via `initializeFileTypeIcons()`, + * or falls back to the default CDN URL. */ export function getFileTypeIconAsUrl( options: FileTypeIconOptions, - baseUrl: string = DEFAULT_BASE_URL, + baseUrl: string = getConfiguredBaseUrl(), ): string | undefined { const { extension, size = DEFAULT_ICON_SIZE, type, imageFileType = 'svg' } = options; const baseIconName = getFileTypeIconNameFromExtensionOrType(extension, type); @@ -36,11 +38,13 @@ export function getFileTypeIconAsUrl( * Given the `fileTypeIconOptions`, similar to `getFileTypeIconProps`, this function returns * an tag DOM element that renders the icon, as a string. * @param options - * @param baseUrl - optionally provide a custom CDN base url to fetch icons from + * @param baseUrl - optionally provide a custom CDN base url to fetch icons from. + * If not provided, uses the URL configured via `initializeFileTypeIcons()`, + * or falls back to the default CDN URL. */ export function getFileTypeIconAsHTMLString( options: FileTypeIconOptions, - baseUrl: string = DEFAULT_BASE_URL, + baseUrl: string = getConfiguredBaseUrl(), ): string | undefined { const url = getFileTypeIconAsUrl(options, baseUrl); diff --git a/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.test.ts b/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.test.ts new file mode 100644 index 00000000000000..f55ba145473f67 --- /dev/null +++ b/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.test.ts @@ -0,0 +1,149 @@ +import { + initializeFileTypeIcons, + getConfiguredBaseUrl, + resetConfiguredBaseUrl, + DEFAULT_BASE_URL, +} from './initializeFileTypeIcons'; +import { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from './getFileTypeIconAsUrl'; + +describe('initializeFileTypeIcons', () => { + // Store original DPR once at the start + const originalDPR = typeof window !== 'undefined' ? window.devicePixelRatio : 1; + + beforeEach(() => { + // Reset to default state before each test + resetConfiguredBaseUrl(); + + // Reset to 1x DPR before each test + if (typeof window !== 'undefined') { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: 1, + }); + } + }); + + afterAll(() => { + // Restore original DPR after all tests + if (typeof window !== 'undefined') { + Object.defineProperty(window, 'devicePixelRatio', { + writable: true, + configurable: true, + value: originalDPR, + }); + } + // Reset to default state + resetConfiguredBaseUrl(); + }); + + describe('getConfiguredBaseUrl', () => { + it('should return DEFAULT_BASE_URL when initializeFileTypeIcons has not been called', () => { + expect(getConfiguredBaseUrl()).toBe(DEFAULT_BASE_URL); + }); + + it('should return DEFAULT_BASE_URL after calling initializeFileTypeIcons with no arguments', () => { + initializeFileTypeIcons(); + expect(getConfiguredBaseUrl()).toBe(DEFAULT_BASE_URL); + }); + + it('should return custom URL after calling initializeFileTypeIcons with custom URL', () => { + const customUrl = 'https://my-custom-cdn.com/icons/'; + initializeFileTypeIcons(customUrl); + expect(getConfiguredBaseUrl()).toBe(customUrl); + }); + + it('should return DEFAULT_BASE_URL after resetConfiguredBaseUrl is called', () => { + const customUrl = 'https://my-custom-cdn.com/icons/'; + initializeFileTypeIcons(customUrl); + expect(getConfiguredBaseUrl()).toBe(customUrl); + + resetConfiguredBaseUrl(); + expect(getConfiguredBaseUrl()).toBe(DEFAULT_BASE_URL); + }); + }); + + describe('integration with getFileTypeIconAsUrl', () => { + it('should use DEFAULT_BASE_URL when initializeFileTypeIcons has not been called', () => { + const result = getFileTypeIconAsUrl({ extension: 'docx', size: 16 }); + expect(result).toBe(`${DEFAULT_BASE_URL}16/docx.svg`); + }); + + it('should use custom URL after initializeFileTypeIcons is called with custom URL', () => { + const customUrl = 'https://my-custom-cdn.com/icons/'; + initializeFileTypeIcons(customUrl); + + const result = getFileTypeIconAsUrl({ extension: 'docx', size: 16 }); + expect(result).toBe(`${customUrl}16/docx.svg`); + }); + + it('should allow explicit baseUrl parameter to override configured URL', () => { + const configuredUrl = 'https://configured-cdn.com/icons/'; + const explicitUrl = 'https://explicit-cdn.com/icons/'; + + initializeFileTypeIcons(configuredUrl); + + // Without explicit baseUrl - uses configured URL + const resultWithoutExplicit = getFileTypeIconAsUrl({ extension: 'docx', size: 16 }); + expect(resultWithoutExplicit).toBe(`${configuredUrl}16/docx.svg`); + + // With explicit baseUrl - uses explicit URL + const resultWithExplicit = getFileTypeIconAsUrl({ extension: 'docx', size: 16 }, explicitUrl); + expect(resultWithExplicit).toBe(`${explicitUrl}16/docx.svg`); + }); + }); + + describe('integration with getFileTypeIconAsHTMLString', () => { + it('should use DEFAULT_BASE_URL when initializeFileTypeIcons has not been called', () => { + const result = getFileTypeIconAsHTMLString({ extension: 'pdf', size: 24 }); + expect(result).toBe(`pdf file icon`); + }); + + it('should use custom URL after initializeFileTypeIcons is called with custom URL', () => { + const customUrl = 'https://my-custom-cdn.com/icons/'; + initializeFileTypeIcons(customUrl); + + const result = getFileTypeIconAsHTMLString({ extension: 'pdf', size: 24 }); + expect(result).toBe(`pdf file icon`); + }); + + it('should allow explicit baseUrl parameter to override configured URL', () => { + const configuredUrl = 'https://configured-cdn.com/icons/'; + const explicitUrl = 'https://explicit-cdn.com/icons/'; + + initializeFileTypeIcons(configuredUrl); + + // Without explicit baseUrl - uses configured URL + const resultWithoutExplicit = getFileTypeIconAsHTMLString({ extension: 'xlsx', size: 32 }); + expect(resultWithoutExplicit).toContain(configuredUrl); + + // With explicit baseUrl - uses explicit URL + const resultWithExplicit = getFileTypeIconAsHTMLString({ extension: 'xlsx', size: 32 }, explicitUrl); + expect(resultWithExplicit).toContain(explicitUrl); + }); + }); + + describe('URL override persistence', () => { + it('should persist custom URL across multiple utility calls', () => { + const customUrl = 'https://persistent-cdn.com/icons/'; + initializeFileTypeIcons(customUrl); + + // Multiple calls should all use the configured URL + expect(getFileTypeIconAsUrl({ extension: 'docx', size: 16 })).toContain(customUrl); + expect(getFileTypeIconAsUrl({ extension: 'pdf', size: 24 })).toContain(customUrl); + expect(getFileTypeIconAsUrl({ extension: 'xlsx', size: 32 })).toContain(customUrl); + expect(getFileTypeIconAsHTMLString({ extension: 'pptx', size: 48 })).toContain(customUrl); + }); + + it('should update configured URL when initializeFileTypeIcons is called again', () => { + const firstUrl = 'https://first-cdn.com/icons/'; + const secondUrl = 'https://second-cdn.com/icons/'; + + initializeFileTypeIcons(firstUrl); + expect(getFileTypeIconAsUrl({ extension: 'docx', size: 16 })).toContain(firstUrl); + + initializeFileTypeIcons(secondUrl); + expect(getFileTypeIconAsUrl({ extension: 'docx', size: 16 })).toContain(secondUrl); + }); + }); +}); diff --git a/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.tsx b/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.tsx index cfee9890512ae1..899dfbb006d599 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.tsx +++ b/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.tsx @@ -10,7 +10,30 @@ 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]; +// Module-level state to track the configured base URL +let _configuredBaseUrl: string | undefined; + +/** + * Returns the configured base URL if `initializeFileTypeIcons` was called with a custom URL, + * otherwise returns the default CDN base URL. + * This is used internally by FileTypeIcon component and utility functions. + */ +export function getConfiguredBaseUrl(): string { + return _configuredBaseUrl ?? DEFAULT_BASE_URL; +} + +/** + * Resets the configured base URL to undefined, causing `getConfiguredBaseUrl` to return + * the default CDN base URL. This is primarily intended for testing purposes. + */ +export function resetConfiguredBaseUrl(): void { + _configuredBaseUrl = undefined; +} + export function initializeFileTypeIcons(baseUrl: string = DEFAULT_BASE_URL, options?: Partial): void { + // Store the configured base URL for use by v9 components and utilities + _configuredBaseUrl = baseUrl; + ICON_SIZES.forEach((size: number) => { _initializeIcons(baseUrl, size, options); }); From e89bd7b6cf7a85105bc251672a985fb464183ea8 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 00:50:38 -0800 Subject: [PATCH 36/69] improved icon size checking, removing unnecessary public exports, fixing tests --- .../FileTypeIcon/FileTypeIcon.test.tsx | 61 ++++++++- .../FileTypeIcon/useFileTypeIcon.ts | 6 +- .../library/src/index.ts | 9 +- .../library/src/testing/index.ts | 9 ++ .../src/utils/getFileTypeIconAsUrl.test.ts | 122 +++++++++++++++++- .../library/src/utils/getFileTypeIconProps.ts | 56 +++++++- .../src/utils/initializeFileTypeIcons.test.ts | 8 +- 7 files changed, 246 insertions(+), 25 deletions(-) create mode 100644 packages/react-components/react-file-type-icons/library/src/testing/index.ts diff --git a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx index f4b2df95e985e0..9ac92d9431070f 100644 --- a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx +++ b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/FileTypeIcon.test.tsx @@ -2,12 +2,10 @@ import * as React from 'react'; import { isConformant } from '../../testing/isConformant'; import { render } from '@testing-library/react'; import { FileTypeIcon } from './FileTypeIcon'; +import type { FileTypeIconProps } from './FileTypeIcon.types'; import { FileIconType } from '../../utils/FileIconType'; -import { - initializeFileTypeIcons, - resetConfiguredBaseUrl, - DEFAULT_BASE_URL, -} from '../../utils/initializeFileTypeIcons'; +import { initializeFileTypeIcons, DEFAULT_BASE_URL } from '../../utils/initializeFileTypeIcons'; +import { resetConfiguredBaseUrl } from '../../testing'; describe('FileTypeIcon', () => { isConformant({ @@ -76,6 +74,59 @@ describe('FileTypeIcon', () => { expect(img).toBeTruthy(); expect(img?.src).toContain('genericfile'); }); + + it('handles compound extensions like .tar.gz by using last extension', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + // gz maps to archive icon + expect(img?.src).toContain('archive'); + }); + + it('handles compound extensions like file.min.js', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + // js maps to code icon + expect(img?.src).toContain('code'); + }); +}); + +describe('FileTypeIcon size fallback', () => { + it('uses next smallest size when 30 is requested (falls back to 24)', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('/24/'); + }); + + it('uses next smallest size when 50 is requested (falls back to 48)', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('/48/'); + }); + + it('uses largest size (96) when size above maximum is requested', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('/96/'); + }); + + it('uses largest size (96) when size 128 is requested', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('/96/'); + }); + + it('uses smallest size (16) when size below minimum is requested', () => { + const result = render(); + const img = result.container.querySelector('img'); + expect(img).toBeTruthy(); + expect(img?.src).toContain('/16/'); + }); }); describe('FileTypeIcon with initializeFileTypeIcons', () => { diff --git a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts index db9181477c03c6..316cbb0067075c 100644 --- a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts +++ b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts @@ -4,6 +4,7 @@ import type { FileTypeIconProps, FileTypeIconState } from './FileTypeIcon.types' import { getFileTypeIconNameFromExtensionOrType, getFileTypeIconSuffix, + getValidIconSize, DEFAULT_ICON_SIZE, } from '../../utils/getFileTypeIconProps'; import { FileIconType } from '../../utils/FileIconType'; @@ -16,7 +17,10 @@ export const useFileTypeIcon_unstable = ( props: FileTypeIconProps, ref: React.Ref, ): FileTypeIconState => { - const { extension, type, size = DEFAULT_ICON_SIZE, imageFileType = 'svg', baseUrl = getConfiguredBaseUrl() } = props; + const { extension, type, size: requestedSize = DEFAULT_ICON_SIZE, imageFileType = 'svg', baseUrl = getConfiguredBaseUrl() } = props; + + // Validate and adjust size to nearest available + const size = getValidIconSize(requestedSize); // Get the base icon name and suffix separately using v8 pattern const baseIconName = getFileTypeIconNameFromExtensionOrType(extension, type); diff --git a/packages/react-components/react-file-type-icons/library/src/index.ts b/packages/react-components/react-file-type-icons/library/src/index.ts index f8f5771663adc9..8c33d5996d47fd 100644 --- a/packages/react-components/react-file-type-icons/library/src/index.ts +++ b/packages/react-components/react-file-type-icons/library/src/index.ts @@ -15,14 +15,9 @@ export { getFileTypeIconProps, getFileTypeIconNameFromExtensionOrType, getFileTypeIconSuffix, + getValidIconSize, DEFAULT_ICON_SIZE, } from './utils/getFileTypeIconProps'; export type { FileTypeIconSize, ImageFileType, FileTypeIconOptions } from './utils/getFileTypeIconProps'; export { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from './utils/getFileTypeIconAsUrl'; -export { - initializeFileTypeIcons, - getConfiguredBaseUrl, - resetConfiguredBaseUrl, - DEFAULT_BASE_URL, - ICON_SIZES, -} from './utils/initializeFileTypeIcons'; +export { initializeFileTypeIcons, DEFAULT_BASE_URL, ICON_SIZES } from './utils/initializeFileTypeIcons'; diff --git a/packages/react-components/react-file-type-icons/library/src/testing/index.ts b/packages/react-components/react-file-type-icons/library/src/testing/index.ts new file mode 100644 index 00000000000000..7ed58323b5b9a5 --- /dev/null +++ b/packages/react-components/react-file-type-icons/library/src/testing/index.ts @@ -0,0 +1,9 @@ +/** + * Testing utilities for @fluentui/react-file-type-icons + * + * These exports are intended for testing purposes only and should not be used in production code. + * They provide access to internal state management functions that allow tests to reset and verify + * the configured base URL state. + */ + +export { getConfiguredBaseUrl, resetConfiguredBaseUrl } from '../utils/initializeFileTypeIcons'; diff --git a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts index f53ee55e9ca51c..b83ea3c4630d15 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts @@ -1,6 +1,6 @@ import { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from './getFileTypeIconAsUrl'; import { DEFAULT_BASE_URL } from './initializeFileTypeIcons'; -import { getFileTypeIconNameFromExtensionOrType } from './getFileTypeIconProps'; +import { getFileTypeIconNameFromExtensionOrType, getValidIconSize } from './getFileTypeIconProps'; import { FileIconType } from './FileIconType'; describe('getFileTypeIconNameFromExtensionOrType', () => { @@ -70,6 +70,126 @@ describe('getFileTypeIconNameFromExtensionOrType', () => { }); }); }); + + describe('with compound extensions', () => { + it('should extract the last extension from .tar.gz', () => { + // .tar.gz should extract 'gz' and map to appropriate icon + const result = getFileTypeIconNameFromExtensionOrType('.tar.gz', undefined); + expect(result).toBe('archive'); // gz maps to archive icon + }); + + it('should extract the last extension from archive.tar.bz2', () => { + const result = getFileTypeIconNameFromExtensionOrType('archive.tar.bz2', undefined); + // bz2 is not a known extension, should fall back to genericfile + expect(result).toBe('genericfile'); + }); + + it('should extract the last extension from file.min.js', () => { + const result = getFileTypeIconNameFromExtensionOrType('file.min.js', undefined); + expect(result).toBe('code'); // js maps to code icon + }); + + it('should extract the last extension from styles.module.css', () => { + const result = getFileTypeIconNameFromExtensionOrType('styles.module.css', undefined); + expect(result).toBe('code'); // css maps to code icon + }); + + it('should handle simple extension with leading dot', () => { + const result = getFileTypeIconNameFromExtensionOrType('.docx', undefined); + expect(result).toBe('docx'); + }); + + it('should handle simple extension without leading dot', () => { + const result = getFileTypeIconNameFromExtensionOrType('pdf', undefined); + expect(result).toBe('pdf'); + }); + }); +}); + +describe('getValidIconSize', () => { + describe('with exact valid sizes', () => { + it('should return exact size when 16 is requested', () => { + expect(getValidIconSize(16)).toBe(16); + }); + + it('should return exact size when 20 is requested', () => { + expect(getValidIconSize(20)).toBe(20); + }); + + it('should return exact size when 24 is requested', () => { + expect(getValidIconSize(24)).toBe(24); + }); + + it('should return exact size when 32 is requested', () => { + expect(getValidIconSize(32)).toBe(32); + }); + + it('should return exact size when 40 is requested', () => { + expect(getValidIconSize(40)).toBe(40); + }); + + it('should return exact size when 48 is requested', () => { + expect(getValidIconSize(48)).toBe(48); + }); + + it('should return exact size when 64 is requested', () => { + expect(getValidIconSize(64)).toBe(64); + }); + + it('should return exact size when 96 is requested', () => { + expect(getValidIconSize(96)).toBe(96); + }); + }); + + describe('with sizes requiring fallback to smaller', () => { + it('should return 16 when 18 is requested (next smallest)', () => { + expect(getValidIconSize(18)).toBe(16); + }); + + it('should return 20 when 22 is requested (next smallest)', () => { + expect(getValidIconSize(22)).toBe(20); + }); + + it('should return 24 when 30 is requested (next smallest)', () => { + expect(getValidIconSize(30)).toBe(24); + }); + + it('should return 32 when 35 is requested (next smallest)', () => { + expect(getValidIconSize(35)).toBe(32); + }); + + it('should return 64 when 80 is requested (next smallest)', () => { + expect(getValidIconSize(80)).toBe(64); + }); + }); + + describe('with sizes larger than maximum', () => { + it('should return 96 when 100 is requested', () => { + expect(getValidIconSize(100)).toBe(96); + }); + + it('should return 96 when 128 is requested', () => { + expect(getValidIconSize(128)).toBe(96); + }); + + it('should return 96 when 256 is requested', () => { + expect(getValidIconSize(256)).toBe(96); + }); + }); + + describe('with sizes smaller than minimum', () => { + it('should return 16 when 10 is requested', () => { + expect(getValidIconSize(10)).toBe(16); + }); + + it('should return 16 when 8 is requested', () => { + expect(getValidIconSize(8)).toBe(16); + }); + + it('should return 16 when 1 is requested', () => { + expect(getValidIconSize(1)).toBe(16); + }); + }); }); describe('getFileTypeIconAsUrl', () => { diff --git a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts index 780fe314408d7e..4e5545ff7839a7 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconProps.ts @@ -1,5 +1,6 @@ import { FileTypeIconMap } from './FileTypeIconMap'; import { FileIconType } from './FileIconType'; +import { ICON_SIZES } from './initializeFileTypeIcons'; let _extensionToIconName: { [key: string]: string }; let _typeToIconName: { [key: number]: string }; @@ -35,6 +36,45 @@ export interface FileTypeIconOptions { imageFileType?: ImageFileType; } +/** + * Gets the nearest valid icon size. If the requested size is not available, + * returns the next smallest available size. If the requested size is larger + * than all available sizes, returns the largest available size (96). + * If the requested size is smaller than all available sizes, returns the smallest (16). + * + * @param size - The requested icon size + * @returns The nearest valid icon size from ICON_SIZES + */ +export function getValidIconSize(size: number): FileTypeIconSize { + // ICON_SIZES is already sorted: [16, 20, 24, 32, 40, 48, 64, 96] + const sortedSizes = ICON_SIZES as FileTypeIconSize[]; + + // If exact match exists, return it + if (sortedSizes.includes(size as FileTypeIconSize)) { + return size as FileTypeIconSize; + } + + // If size is larger than the largest available, return the largest + if (size > sortedSizes[sortedSizes.length - 1]) { + return sortedSizes[sortedSizes.length - 1]; + } + + // If size is smaller than the smallest available, return the smallest + if (size < sortedSizes[0]) { + return sortedSizes[0]; + } + + // Find the next smallest available size + for (let i = sortedSizes.length - 1; i >= 0; i--) { + if (sortedSizes[i] <= size) { + return sortedSizes[i]; + } + } + + // Fallback to default (should never reach here) + return DEFAULT_ICON_SIZE; +} + /** * This function returns properties for a file type icon given the FileTypeIconOptions. * It accounts for different device pixel ratios. For example, @@ -83,8 +123,10 @@ export function getFileTypeIconNameFromExtensionOrType( } } - // Strip periods, force lowercase. - extension = extension.replace('.', '').toLowerCase(); + // Extract only the last extension (handles compound extensions like .tar.gz -> gz) + // and force lowercase + const lastDotIndex = extension.lastIndexOf('.'); + extension = (lastDotIndex >= 0 ? extension.substring(lastDotIndex + 1) : extension).toLowerCase(); return _extensionToIconName[extension] || GENERIC_FILE; } else if (type) { if (!_typeToIconName) { @@ -111,16 +153,20 @@ export function getFileTypeIconNameFromExtensionOrType( /** * Gets the suffix for the icon name based on size, file type, and device pixel ratio. - * @param size - The icon size in pixels + * If the requested size is not available, it will be adjusted to the nearest valid size. + * @param size - The icon size in pixels (will be validated against available sizes) * @param imageFileType - The image file type ('svg' or 'png') * @param win - Optional window object for testing * @returns The icon name suffix */ export function getFileTypeIconSuffix( - size: FileTypeIconSize, + size: FileTypeIconSize | number, imageFileType: ImageFileType = 'svg', win?: Window, ): string { + // Validate and adjust size to nearest available + const validSize = getValidIconSize(size); + // eslint-disable-next-line @nx/workspace-no-restricted-globals win ??= typeof window !== 'undefined' ? window : ({ devicePixelRatio: 1 } as Window); const devicePixelRatio: number = win.devicePixelRatio; @@ -143,5 +189,5 @@ export function getFileTypeIconSuffix( } } - return size + devicePixelRatioSuffix + '_' + imageFileType; + return validSize + devicePixelRatioSuffix + '_' + imageFileType; } diff --git a/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.test.ts b/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.test.ts index f55ba145473f67..c94feed6a513ac 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.test.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/initializeFileTypeIcons.test.ts @@ -1,9 +1,5 @@ -import { - initializeFileTypeIcons, - getConfiguredBaseUrl, - resetConfiguredBaseUrl, - DEFAULT_BASE_URL, -} from './initializeFileTypeIcons'; +import { initializeFileTypeIcons, DEFAULT_BASE_URL } from './initializeFileTypeIcons'; +import { getConfiguredBaseUrl, resetConfiguredBaseUrl } from '../testing'; import { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from './getFileTypeIconAsUrl'; describe('initializeFileTypeIcons', () => { From 9af6811a5c43158a0ef0144af2931c11d801be06 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 10:05:16 -0800 Subject: [PATCH 37/69] cleanup stray API file --- .../etc/file-type-icons-preview.api.md | 152 ------------------ 1 file changed, 152 deletions(-) delete mode 100644 packages/react-components/react-file-type-icons/library/etc/file-type-icons-preview.api.md diff --git a/packages/react-components/react-file-type-icons/library/etc/file-type-icons-preview.api.md b/packages/react-components/react-file-type-icons/library/etc/file-type-icons-preview.api.md deleted file mode 100644 index 33f598e1176e29..00000000000000 --- a/packages/react-components/react-file-type-icons/library/etc/file-type-icons-preview.api.md +++ /dev/null @@ -1,152 +0,0 @@ -## API Report File for "@fluentui/react-file-type-icons" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import type { ComponentProps } from '@fluentui/react-utilities'; -import type { ComponentState } from '@fluentui/react-utilities'; -import type { ForwardRefComponent } from '@fluentui/react-utilities'; -import type { IIconOptions } from '@fluentui/style-utilities'; -import type { JSXElement } from '@fluentui/react-utilities'; -import * as React_2 from 'react'; -import type { Slot } from '@fluentui/react-utilities'; -import type { SlotClassNames } from '@fluentui/react-utilities'; - -// @public (undocumented) -export const DEFAULT_BASE_URL = "https://res.cdn.office.net/files/fabric-cdn-prod_20251107.003/assets/item-types/"; - -// @public (undocumented) -export const DEFAULT_ICON_SIZE: FileTypeIconSize; - -// @public -export enum FileIconType { - // (undocumented) - album = 21,// Start at 1 so it will evaluate as "truthy" - // (undocumented) - desktopFolder = 9, - // (undocumented) - docset = 1, - // (undocumented) - documentsFolder = 10, - // (undocumented) - folder = 2, - // (undocumented) - form = 14, - // (undocumented) - genericFile = 3, - // (undocumented) - linkedFolder = 12, - // (undocumented) - list = 13, - // (undocumented) - listForm = 22, - // (undocumented) - listItem = 4, - // (undocumented) - loopworkspace = 17, - // (undocumented) - multiple = 6, - // (undocumented) - news = 8, - // (undocumented) - picturesFolder = 11, - // (undocumented) - planner = 18, - // (undocumented) - playlist = 16, - // (undocumented) - portfolio = 20, - // (undocumented) - sharedFolder = 5, - // (undocumented) - stream = 7, - // (undocumented) - sway = 15, - // (undocumented) - todoItem = 19 -} - -// @public -export const FileTypeIcon: ForwardRefComponent; - -// @public (undocumented) -export const fileTypeIconClassNames: SlotClassNames; - -// @public -export const FileTypeIconMap: { - [key: string]: { - extensions?: string[]; - types?: FileIconType[]; - }; -}; - -// @public (undocumented) -export interface FileTypeIconOptions { - extension?: string; - imageFileType?: ImageFileType; - size?: FileTypeIconSize; - type?: FileIconType; -} - -// @public (undocumented) -export type FileTypeIconProps = ComponentProps & { - extension?: string; - type?: FileIconType; - size?: FileTypeIconSize; - imageFileType?: ImageFileType; - baseUrl?: string; -}; - -// @public (undocumented) -export type FileTypeIconSize = 16 | 20 | 24 | 32 | 40 | 48 | 64 | 96; - -// @public (undocumented) -export type FileTypeIconSlots = { - root: Slot<'img'>; -}; - -// @public (undocumented) -export type FileTypeIconState = ComponentState & Required> & { - iconUrl: string; -}; - -// @public -export function getFileTypeIconAsHTMLString(options: FileTypeIconOptions, baseUrl?: string): string | undefined; - -// @public -export function getFileTypeIconAsUrl(options: FileTypeIconOptions, baseUrl?: string): string | undefined; - -// @public -export function getFileTypeIconNameFromExtensionOrType(extension: string | undefined, type: FileIconType | undefined): string; - -// @public -export function getFileTypeIconProps(options: FileTypeIconOptions): { - iconName: string; - 'aria-label'?: string; -}; - -// @public -export function getFileTypeIconSuffix(size: FileTypeIconSize, imageFileType?: ImageFileType, win?: Window): string; - -// @public (undocumented) -export const ICON_SIZES: number[]; - -// @public (undocumented) -export type ImageFileType = 'svg' | 'png'; - -// @public (undocumented) -export function initializeFileTypeIcons(baseUrl?: string, options?: Partial): void; - -// @public -export const renderFileTypeIcon_unstable: (state: FileTypeIconState) => JSXElement; - -// @public -export const useFileTypeIcon_unstable: (props: FileTypeIconProps, ref: React_2.Ref) => FileTypeIconState; - -// @public -export const useFileTypeIconStyles_unstable: (state: FileTypeIconState) => FileTypeIconState; - -// (No @packageDocumentation comment for this package) - -``` From df430ed9d298633cde1baa1031d61198e671e154 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 11:01:38 -0800 Subject: [PATCH 38/69] changing v8 package name and adding publishConfig so it emits to the same npm --- apps/public-docsite/package.json | 2 +- .../pages/Styles/FileTypeIconsPage/FileTypeIconsPage.tsx | 2 +- apps/public-docsite/src/root.tsx | 2 +- .../library/etc/react-file-type-icons.api.md | 5 ++++- packages/react-examples/package.json | 2 +- .../FileTypeIcon/FileTypeIcon.Basic.Example.tsx | 2 +- .../react-examples/src/react-experiments/demo/index.tsx | 2 +- packages/react-file-type-icons/package.json | 7 +++++-- tsconfig.base.v8.json | 2 +- 9 files changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/public-docsite/package.json b/apps/public-docsite/package.json index 84a013c8e8fac3..776e69559c7e75 100644 --- a/apps/public-docsite/package.json +++ b/apps/public-docsite/package.json @@ -39,7 +39,7 @@ "@fluentui/react-examples": "*", "@fluentui/react-experiments": "*", "@fluentui/fluent2-theme": "*", - "@fluentui/react-file-type-icons": "*", + "@fluentui/react-file-type-icons-v8": "*", "@fluentui/react-icons-mdl2": "*", "@fluentui/react-icons-mdl2-branded": "*", "@fluentui/set-version": "*", diff --git a/apps/public-docsite/src/pages/Styles/FileTypeIconsPage/FileTypeIconsPage.tsx b/apps/public-docsite/src/pages/Styles/FileTypeIconsPage/FileTypeIconsPage.tsx index 0036be34cb055e..e120a777e78212 100644 --- a/apps/public-docsite/src/pages/Styles/FileTypeIconsPage/FileTypeIconsPage.tsx +++ b/apps/public-docsite/src/pages/Styles/FileTypeIconsPage/FileTypeIconsPage.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { Icon, Link } from '@fluentui/react'; -import { getFileTypeIconProps } from '@fluentui/react-file-type-icons'; +import { getFileTypeIconProps } from '@fluentui/react-file-type-icons-v8'; import { Markdown, MarkdownHeader, IPageSectionProps } from '@fluentui/react-docsite-components/lib/index2'; import { IStylesPageProps, StylesAreaPage } from '../StylesAreaPage'; import { FileTypeIconsPageProps } from './FileTypeIconsPage.doc'; diff --git a/apps/public-docsite/src/root.tsx b/apps/public-docsite/src/root.tsx index 8cd4d118a27760..4373ad99366451 100644 --- a/apps/public-docsite/src/root.tsx +++ b/apps/public-docsite/src/root.tsx @@ -1,5 +1,5 @@ import { registerIcons, on, KeyCodes, setRTL } from '@fluentui/react'; -import { initializeFileTypeIcons } from '@fluentui/react-file-type-icons'; +import { initializeFileTypeIcons } from '@fluentui/react-file-type-icons-v8'; import { createSite } from './utilities/createSite'; import * as platformPickerStyles from '@fluentui/react-docsite-components/lib/components/PlatformPicker/PlatformPicker.module.scss'; import { SiteDefinition } from './SiteDefinition/index'; diff --git a/packages/react-components/react-file-type-icons/library/etc/react-file-type-icons.api.md b/packages/react-components/react-file-type-icons/library/etc/react-file-type-icons.api.md index 33f598e1176e29..ba285fb0bb30a4 100644 --- a/packages/react-components/react-file-type-icons/library/etc/react-file-type-icons.api.md +++ b/packages/react-components/react-file-type-icons/library/etc/react-file-type-icons.api.md @@ -127,7 +127,10 @@ export function getFileTypeIconProps(options: FileTypeIconOptions): { }; // @public -export function getFileTypeIconSuffix(size: FileTypeIconSize, imageFileType?: ImageFileType, win?: Window): string; +export function getFileTypeIconSuffix(size: FileTypeIconSize | number, imageFileType?: ImageFileType, win?: Window): string; + +// @public +export function getValidIconSize(size: number): FileTypeIconSize; // @public (undocumented) export const ICON_SIZES: number[]; diff --git a/packages/react-examples/package.json b/packages/react-examples/package.json index 5bfd0be85071cc..2a7bf32375ef6e 100644 --- a/packages/react-examples/package.json +++ b/packages/react-examples/package.json @@ -36,7 +36,7 @@ "@fluentui/react-charting": "^5.25.2", "@fluentui/react-docsite-components": "^8.15.0", "@fluentui/react-experiments": "^8.16.1", - "@fluentui/react-file-type-icons": "^8.15.1", + "@fluentui/react-file-type-icons-v8": "^8.15.1", "@fluentui/react-focus": "^8.10.1", "@fluentui/react-hooks": "^8.10.1", "@fluentui/react-icons-mdl2": "^1.4.1", diff --git a/packages/react-examples/src/react-experiments/FileTypeIcon/FileTypeIcon.Basic.Example.tsx b/packages/react-examples/src/react-experiments/FileTypeIcon/FileTypeIcon.Basic.Example.tsx index a28997259c3962..b6effbf51a4ce6 100644 --- a/packages/react-examples/src/react-experiments/FileTypeIcon/FileTypeIcon.Basic.Example.tsx +++ b/packages/react-examples/src/react-experiments/FileTypeIcon/FileTypeIcon.Basic.Example.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { Icon } from '@fluentui/react/lib/Icon'; -import { getFileTypeIconProps, FileIconType, initializeFileTypeIcons } from '@fluentui/react-file-type-icons'; +import { getFileTypeIconProps, FileIconType, initializeFileTypeIcons } from '@fluentui/react-file-type-icons-v8'; import type { JSXElement } from '@fluentui/utilities'; initializeFileTypeIcons(undefined); diff --git a/packages/react-examples/src/react-experiments/demo/index.tsx b/packages/react-examples/src/react-experiments/demo/index.tsx index 6470f6821f687d..f70c2b123f3548 100644 --- a/packages/react-examples/src/react-experiments/demo/index.tsx +++ b/packages/react-examples/src/react-experiments/demo/index.tsx @@ -1,7 +1,7 @@ import { createDemoApp } from '@fluentui/react-docsite-components'; import { AppDefinition } from './AppDefinition'; import { GettingStartedPage } from './GettingStartedPage'; -import { initializeFileTypeIcons } from '@fluentui/react-file-type-icons'; +import { initializeFileTypeIcons } from '@fluentui/react-file-type-icons-v8'; import { initializeFolderCovers } from '@fluentui/react-experiments'; initializeFileTypeIcons(); diff --git a/packages/react-file-type-icons/package.json b/packages/react-file-type-icons/package.json index 0e925c0894507c..ca5cda82661fdb 100644 --- a/packages/react-file-type-icons/package.json +++ b/packages/react-file-type-icons/package.json @@ -1,7 +1,7 @@ { - "name": "@fluentui/react-file-type-icons", + "name": "@fluentui/react-file-type-icons-v8", "version": "8.15.1", - "description": "Fluent UI React file type icon set.", + "description": "Fluent UI React file type icon set for FluentUI v8", "main": "lib-commonjs/index.js", "module": "lib/index.js", "sideEffects": [ @@ -13,6 +13,9 @@ "url": "https://github.com/microsoft/fluentui" }, "license": "MIT", + "publishConfig": { + "name": "@fluentui/react-file-type-icons" + }, "scripts": { "build": "just-scripts build", "clean": "just-scripts clean" diff --git a/tsconfig.base.v8.json b/tsconfig.base.v8.json index 16cf17379d5982..87e00e6671e86f 100644 --- a/tsconfig.base.v8.json +++ b/tsconfig.base.v8.json @@ -27,7 +27,7 @@ "@fluentui/react-date-time": ["packages/react-date-time/src/index.ts"], "@fluentui/react-experiments": ["packages/react-experiments/src/index.ts"], "@fluentui/react-experiments/lib/*": ["packages/react-experiments/src/*"], - "@fluentui/react-file-type-icons": ["packages/react-file-type-icons/src/index.ts"], + "@fluentui/react-file-type-icons-v8": ["packages/react-file-type-icons/src/index.ts"], "@fluentui/react-focus": ["packages/react-focus/src/index.ts"], "@fluentui/react-hooks": ["packages/react-hooks/src/index.ts"], "@fluentui/scheme-utilities": ["packages/scheme-utilities/src/index.ts"], From 02048244fa52875a24d8da3aaaa345d51f072ae0 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 11:23:25 -0800 Subject: [PATCH 39/69] updating swc packages and lint tweaks --- package.json | 7 +- yarn.lock | 334 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 270 insertions(+), 71 deletions(-) diff --git a/package.json b/package.json index 9b582500103a8a..8b22087fbb8144 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,8 @@ "@babel/plugin-proposal-class-properties": "7.18.6", "@babel/plugin-proposal-decorators": "7.24.6", "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6", - "@babel/plugin-proposal-optional-chaining": "7.21.0", "@babel/plugin-proposal-object-rest-spread": "7.20.7", + "@babel/plugin-proposal-optional-chaining": "7.21.0", "@babel/plugin-syntax-dynamic-import": "7.8.3", "@babel/plugin-syntax-object-rest-spread": "7.8.3", "@babel/plugin-transform-runtime": "7.24.6", @@ -106,7 +106,7 @@ "@storybook/react-webpack5": "8.6.14", "@storybook/theming": "8.6.14", "@swc/cli": "0.7.7", - "@swc/core": "1.11.24", + "@swc/core": "1.15.3", "@swc/helpers": "0.5.1", "@swc/jest": "0.2.38", "@testing-library/dom": "10.4.0", @@ -166,8 +166,8 @@ "@types/webpack-hot-middleware": "2.25.9", "@types/yargs": "13.0.11", "@types/yargs-unparser": "2.0.1", - "@typescript-eslint/rule-tester": "8.46.2", "@typescript-eslint/eslint-plugin": "^8.46.2", + "@typescript-eslint/rule-tester": "8.46.2", "autoprefixer": "10.2.1", "babel-jest": "29.7.0", "babel-loader": "9.1.3", @@ -338,6 +338,7 @@ "dependencies": { "@fluentui/react-icons-northstar": "0.66.5", "@fluentui/react-northstar": "0.66.5", + "@swc-node/register": "1.11.1", "copy-to-clipboard": "3.3.1" }, "license": "MIT", diff --git a/yarn.lock b/yarn.lock index dc554e366e92ec..1afa02e48e7ed6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1589,6 +1589,14 @@ "@emnapi/wasi-threads" "1.0.1" tslib "^2.4.0" +"@emnapi/core@^1.5.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.7.1.tgz#3a79a02dbc84f45884a1806ebb98e5746bdfaac4" + integrity sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg== + dependencies: + "@emnapi/wasi-threads" "1.1.0" + tslib "^2.4.0" + "@emnapi/runtime@^1.1.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.4.0.tgz#8f509bf1059a5551c8fe829a1c4e91db35fdfbee" @@ -1596,6 +1604,13 @@ dependencies: tslib "^2.4.0" +"@emnapi/runtime@^1.5.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.7.1.tgz#a73784e23f5d57287369c808197288b52276b791" + integrity sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA== + dependencies: + tslib "^2.4.0" + "@emnapi/wasi-threads@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz#d7ae71fd2166b1c916c6cd2d0df2ef565a2e1a5b" @@ -1603,6 +1618,13 @@ dependencies: tslib "^2.4.0" +"@emnapi/wasi-threads@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf" + integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ== + dependencies: + tslib "^2.4.0" + "@emotion/hash@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" @@ -2828,6 +2850,15 @@ "@emnapi/runtime" "^1.1.0" "@tybys/wasm-util" "^0.9.0" +"@napi-rs/wasm-runtime@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz#dcfea99a75f06209a235f3d941e3460a51e9b14c" + integrity sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw== + dependencies: + "@emnapi/core" "^1.5.0" + "@emnapi/runtime" "^1.5.0" + "@tybys/wasm-util" "^0.10.1" + "@nevware21/ts-async@>= 0.5.2 < 2.x": version "0.5.2" resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.5.2.tgz#a41883dc6ccc4666bdf156e92f35f3003fd3f6f0" @@ -3160,6 +3191,103 @@ css-tree "^3.0.0" nanoid "^5.0.8" +"@oxc-resolver/binding-android-arm-eabi@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.14.0.tgz#98a6b41316d9283c89dac4261ef2b3d3458943ec" + integrity sha512-jB47iZ/thvhE+USCLv+XY3IknBbkKr/p7OBsQDTHode/GPw+OHRlit3NQ1bjt1Mj8V2CS7iHdSDYobZ1/0gagQ== + +"@oxc-resolver/binding-android-arm64@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.14.0.tgz#1a2f24785dc4b8f86eebe5873f0fcd8ba850fd54" + integrity sha512-XFJ9t7d/Cz+dWLyqtTy3Xrekz+qqN4hmOU2iOUgr7u71OQsPUHIIeS9/wKanEK0l413gPwapIkyc5x9ltlOtyw== + +"@oxc-resolver/binding-darwin-arm64@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.14.0.tgz#67cb9b66b060e9a29f6c8d9549669fa061ff6006" + integrity sha512-gwehBS9smA1mzK8frDsmUCHz+6baJVwkKF6qViHhoqA3kRKvIZ3k6WNP4JmF19JhOiGxRcoPa8gZRfzNgXwP2A== + +"@oxc-resolver/binding-darwin-x64@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.14.0.tgz#b7ab6433a6babc8df133497edd847894d3b10061" + integrity sha512-5wwJvfuoahKiAqqAsMLOI28rqdh3P2K7HkjIWUXNMWAZq6ErX0L5rwJzu6T32+Zxw3k18C7R9IS4wDq/3Ar+6w== + +"@oxc-resolver/binding-freebsd-x64@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.14.0.tgz#a7910fc9ad69c12ba227a57580b2053cffc623f0" + integrity sha512-MWTt+LOQNcQ6fa+Uu5VikkihLi1PSIrQqqp0QD44k2AORasNWl0jRGBTcMSBIgNe82qEQWYvlGzvOEEOBp01Og== + +"@oxc-resolver/binding-linux-arm-gnueabihf@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.14.0.tgz#c06d530315f4df3151f44ee3d10488a426fee359" + integrity sha512-b6/IBqYrS3o0XiLVBsnex/wK8pTTK+hbGfAMOHVU6p7DBpwPPLgC/tav4IXoOIUCssTFz7aWh/xtUok0swn8VQ== + +"@oxc-resolver/binding-linux-arm-musleabihf@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.14.0.tgz#50be40fb4a4cdbbb600198bfc9a1f3a6f4233d16" + integrity sha512-o2Qh5+y5YoqVK6YfzkalHdpmQ5bkbGGxuLg1pZLQ1Ift0x+Vix7DaFEpdCl5Z9xvYXogd/TwOlL0TPl4+MTFLA== + +"@oxc-resolver/binding-linux-arm64-gnu@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.14.0.tgz#62fc245a4e83bd2d932de5865cdea3240cf08097" + integrity sha512-lk8mCSg0Tg4sEG73RiPjb7keGcEPwqQnBHX3Z+BR2SWe+qNHpoHcyFMNafzSvEC18vlxC04AUSoa6kJl/C5zig== + +"@oxc-resolver/binding-linux-arm64-musl@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.14.0.tgz#c90bfd0dde8466f982fed7e9332b39c60ffbe682" + integrity sha512-KykeIVhCM7pn93ABa0fNe8vk4XvnbfZMELne2s6P9tdJH9KMBsCFBi7a2BmSdUtTqWCAJokAcm46lpczU52Xaw== + +"@oxc-resolver/binding-linux-ppc64-gnu@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.14.0.tgz#a9fc775ec2db68761df8437ec11b015c46741b34" + integrity sha512-QqPPWAcZU/jHAuam4f3zV8OdEkYRPD2XR0peVet3hoMMgsihR3Lhe7J/bLclmod297FG0+OgBYQVMh2nTN6oWA== + +"@oxc-resolver/binding-linux-riscv64-gnu@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.14.0.tgz#8002c80ba035e26d476fbad50e0b149b9a16079c" + integrity sha512-DunWA+wafeG3hj1NADUD3c+DRvmyVNqF5LSHVUWA2bzswqmuEZXl3VYBSzxfD0j+UnRTFYLxf27AMptoMsepYg== + +"@oxc-resolver/binding-linux-riscv64-musl@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.14.0.tgz#6a3c20f55295626572be80de82b4b4a927d5b073" + integrity sha512-4SRvwKTTk2k67EQr9Ny4NGf/BhlwggCI1CXwBbA9IV4oP38DH8b+NAPxDY0ySGRsWbPkG92FYOqM4AWzG4GSgA== + +"@oxc-resolver/binding-linux-s390x-gnu@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.14.0.tgz#c8031033466a64206ce59bbc771c43c135843495" + integrity sha512-hZKvkbsurj4JOom//R1Ab2MlC4cGeVm5zzMt4IsS3XySQeYjyMJ5TDZ3J5rQ8bVj3xi4FpJU2yFZ72GApsHQ6A== + +"@oxc-resolver/binding-linux-x64-gnu@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.14.0.tgz#7ef94d565dd4be964df1b80e34b515339652f358" + integrity sha512-hABxQXFXJurivw+0amFdeEcK67cF1BGBIN1+sSHzq3TRv4RoG8n5q2JE04Le2n2Kpt6xg4Y5+lcv+rb2mCJLgQ== + +"@oxc-resolver/binding-linux-x64-musl@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.14.0.tgz#2d4226bc06ad336f4854dfd3709f566c02683da6" + integrity sha512-Ln73wUB5migZRvC7obAAdqVwvFvk7AUs2JLt4g9QHr8FnqivlsjpUC9Nf2ssrybdjyQzEMjttUxPZz6aKPSAHw== + +"@oxc-resolver/binding-wasm32-wasi@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.14.0.tgz#c8a048253fa58e239b69f89734d954a631434a9e" + integrity sha512-z+NbELmCOKNtWOqEB5qDfHXOSWB3kGQIIehq6nHtZwHLzdVO2oBq6De/ayhY3ygriC1XhgaIzzniY7jgrNl4Kw== + dependencies: + "@napi-rs/wasm-runtime" "^1.0.7" + +"@oxc-resolver/binding-win32-arm64-msvc@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.14.0.tgz#b0cdf4f7ad4a58995b6b538c844b5b9a2efb7edb" + integrity sha512-Ft0+qd7HSO61qCTLJ4LCdBGZkpKyDj1rG0OVSZL1DxWQoh97m7vEHd7zAvUtw8EcWjOMBQuX4mfRap/x2MOCpQ== + +"@oxc-resolver/binding-win32-ia32-msvc@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.14.0.tgz#616a641967db9fcacebdc06443fad29dcda70a42" + integrity sha512-o54jYNSfGdPxHSvXEhZg8FOV3K99mJ1f7hb1alRFb+Yec1GQXNrJXxZPIxNMYeFT13kwAWB7zuQ0HZLnDHFxfw== + +"@oxc-resolver/binding-win32-x64-msvc@11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.14.0.tgz#53e9a5257332da71cce15f09e42e3b1927156370" + integrity sha512-j97icaORyM6A7GjgmUzfn7V+KGzVvctRA+eAlJb0c2OQNaETFxl6BXZdnGBDb+6oA0Y4Sr/wnekd1kQ0aVyKGg== + "@phenomnomnominal/tsquery@6.1.3", "@phenomnomnominal/tsquery@~5.0.1": version "6.1.3" resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-6.1.3.tgz#5e819403da2fa6a64b009f1876278fb105ec6b55" @@ -3942,6 +4070,32 @@ "@types/express" "^4.7.0" file-system-cache "2.3.0" +"@swc-node/core@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@swc-node/core/-/core-1.14.1.tgz#b3f85f9ac055faee1a3454a0cf71bb953c814f1a" + integrity sha512-jrt5GUaZUU6cmMS+WTJEvGvaB6j1YNKPHPzC2PUi2BjaFbtxURHj6641Az6xN7b665hNniAIdvjxWcRml5yCnw== + +"@swc-node/register@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@swc-node/register/-/register-1.11.1.tgz#bc353f9df7d9c6f0e7f067fe2b3a7552fd667698" + integrity sha512-VQ0hJ5jX31TVv/fhZx4xJRzd8pwn6VvzYd2tGOHHr2TfXGCBixZoqdPDXTiEoJLCTS2MmvBf6zyQZZ0M8aGQCQ== + dependencies: + "@swc-node/core" "^1.14.1" + "@swc-node/sourcemap-support" "^0.6.1" + colorette "^2.0.20" + debug "^4.4.1" + oxc-resolver "^11.6.1" + pirates "^4.0.7" + tslib "^2.8.1" + +"@swc-node/sourcemap-support@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@swc-node/sourcemap-support/-/sourcemap-support-0.6.1.tgz#579bd0cfebc9cea51dd0de7c59c47ecd07b9e505" + integrity sha512-ovltDVH5QpdHXZkW138vG4+dgcNsxfwxHVoV6BtmTbz2KKl1A8ZSlbdtxzzfNjCjbpayda8Us9eMtcHobm38dA== + dependencies: + source-map-support "^0.5.21" + tslib "^2.8.1" + "@swc/cli@0.7.7": version "0.7.7" resolved "https://registry.yarnpkg.com/@swc/cli/-/cli-0.7.7.tgz#b367daba7db5a25fdcdbefe6a80f4c49c300d5fc" @@ -3957,74 +4111,74 @@ slash "3.0.0" source-map "^0.7.3" -"@swc/core-darwin-arm64@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.24.tgz#c9fcc9c4bad0511fed26210449556d2b33fb2d9a" - integrity sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA== - -"@swc/core-darwin-x64@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.11.24.tgz#048ea3ee43281264a62fccb5a944b76d1c56eb24" - integrity sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ== - -"@swc/core-linux-arm-gnueabihf@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.24.tgz#f01ba657a81c67d8fb9f681712e65abf1324cec6" - integrity sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw== - -"@swc/core-linux-arm64-gnu@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.24.tgz#9aefca7f7f87c8312c2fa714c1eb731411d8596c" - integrity sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg== - -"@swc/core-linux-arm64-musl@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.24.tgz#e4805484779bbc59b639eab4f8e45166f3d7a4f7" - integrity sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw== - -"@swc/core-linux-x64-gnu@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.24.tgz#e8d8cc50a49903880944379590b73733e150a5d4" - integrity sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg== - -"@swc/core-linux-x64-musl@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.24.tgz#f3c46212eb8a793f6a42a36b2a9017a9b1462737" - integrity sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw== - -"@swc/core-win32-arm64-msvc@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.24.tgz#b1c3327d81a5f94415ac0b1713e192df1c121fbd" - integrity sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ== - -"@swc/core-win32-ia32-msvc@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.24.tgz#6a944dd6111ec5fae3cf5925b73701e49b109edc" - integrity sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ== - -"@swc/core-win32-x64-msvc@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.24.tgz#eebb5d5ece2710aeb25cc58bd7c5c4c2c046f030" - integrity sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w== - -"@swc/core@1.11.24": - version "1.11.24" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.11.24.tgz#340425648296964f815c940b8da00fcdb1ff2abd" - integrity sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg== +"@swc/core-darwin-arm64@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.3.tgz#bd0bd3ab7730e3ffa64cf200c0ed7c572cbaba97" + integrity sha512-AXfeQn0CvcQ4cndlIshETx6jrAM45oeUrK8YeEY6oUZU/qzz0Id0CyvlEywxkWVC81Ajpd8TQQ1fW5yx6zQWkQ== + +"@swc/core-darwin-x64@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.15.3.tgz#502b1e1c680df6b962265ca81a0c1a23e6ff070f" + integrity sha512-p68OeCz1ui+MZYG4wmfJGvcsAcFYb6Sl25H9TxWl+GkBgmNimIiRdnypK9nBGlqMZAcxngNPtnG3kEMNnvoJ2A== + +"@swc/core-linux-arm-gnueabihf@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.3.tgz#e32cc6a2e06a75060d6f598ba2ca6f96c5c0cc43" + integrity sha512-Nuj5iF4JteFgwrai97mUX+xUOl+rQRHqTvnvHMATL/l9xE6/TJfPBpd3hk/PVpClMXG3Uvk1MxUFOEzM1JrMYg== + +"@swc/core-linux-arm64-gnu@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.3.tgz#9b9861bc44059e393d4baf98b3cd3d6c4ea6f521" + integrity sha512-2Nc/s8jE6mW2EjXWxO/lyQuLKShcmTrym2LRf5Ayp3ICEMX6HwFqB1EzDhwoMa2DcUgmnZIalesq2lG3krrUNw== + +"@swc/core-linux-arm64-musl@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.3.tgz#f6388743e5a159018bd468e8f710940b2614384b" + integrity sha512-j4SJniZ/qaZ5g8op+p1G9K1z22s/EYGg1UXIb3+Cg4nsxEpF5uSIGEE4mHUfA70L0BR9wKT2QF/zv3vkhfpX4g== + +"@swc/core-linux-x64-gnu@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.3.tgz#15fea551c7a3aeb1bdc3ad5c652d73c9321ddba8" + integrity sha512-aKttAZnz8YB1VJwPQZtyU8Uk0BfMP63iDMkvjhJzRZVgySmqt/apWSdnoIcZlUoGheBrcqbMC17GGUmur7OT5A== + +"@swc/core-linux-x64-musl@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.3.tgz#d3f17bab4ffcadbb47f135e6a14d6f3e401af289" + integrity sha512-oe8FctPu1gnUsdtGJRO2rvOUIkkIIaHqsO9xxN0bTR7dFTlPTGi2Fhk1tnvXeyAvCPxLIcwD8phzKg6wLv9yug== + +"@swc/core-win32-arm64-msvc@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.3.tgz#9da386df7fed00b3473bcf4281ff3fcd14726d2c" + integrity sha512-L9AjzP2ZQ/Xh58e0lTRMLvEDrcJpR7GwZqAtIeNLcTK7JVE+QineSyHp0kLkO1rttCHyCy0U74kDTj0dRz6raA== + +"@swc/core-win32-ia32-msvc@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.3.tgz#c398d4f0f10ffec2151a79733ee1ce86a945a1ea" + integrity sha512-B8UtogMzErUPDWUoKONSVBdsgKYd58rRyv2sHJWKOIMCHfZ22FVXICR4O/VwIYtlnZ7ahERcjayBHDlBZpR0aw== + +"@swc/core-win32-x64-msvc@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.3.tgz#715596b034a654c82b03ef734a9b44c29bcd3a68" + integrity sha512-SpZKMR9QBTecHeqpzJdYEfgw30Oo8b/Xl6rjSzBt1g0ZsXyy60KLXrp6IagQyfTYqNYE/caDvwtF2FPn7pomog== + +"@swc/core@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.15.3.tgz#2d0a5c4ac4c180c3dbf2f6d5d958b9fcbaa9755f" + integrity sha512-Qd8eBPkUFL4eAONgGjycZXj1jFCBW8Fd+xF0PzdTlBCWQIV1xnUT7B93wUANtW3KGjl3TRcOyxwSx/u/jyKw/Q== dependencies: "@swc/counter" "^0.1.3" - "@swc/types" "^0.1.21" + "@swc/types" "^0.1.25" optionalDependencies: - "@swc/core-darwin-arm64" "1.11.24" - "@swc/core-darwin-x64" "1.11.24" - "@swc/core-linux-arm-gnueabihf" "1.11.24" - "@swc/core-linux-arm64-gnu" "1.11.24" - "@swc/core-linux-arm64-musl" "1.11.24" - "@swc/core-linux-x64-gnu" "1.11.24" - "@swc/core-linux-x64-musl" "1.11.24" - "@swc/core-win32-arm64-msvc" "1.11.24" - "@swc/core-win32-ia32-msvc" "1.11.24" - "@swc/core-win32-x64-msvc" "1.11.24" + "@swc/core-darwin-arm64" "1.15.3" + "@swc/core-darwin-x64" "1.15.3" + "@swc/core-linux-arm-gnueabihf" "1.15.3" + "@swc/core-linux-arm64-gnu" "1.15.3" + "@swc/core-linux-arm64-musl" "1.15.3" + "@swc/core-linux-x64-gnu" "1.15.3" + "@swc/core-linux-x64-musl" "1.15.3" + "@swc/core-win32-arm64-msvc" "1.15.3" + "@swc/core-win32-ia32-msvc" "1.15.3" + "@swc/core-win32-x64-msvc" "1.15.3" "@swc/counter@^0.1.3": version "0.1.3" @@ -4047,7 +4201,7 @@ "@swc/counter" "^0.1.3" jsonc-parser "^3.2.0" -"@swc/types@^0.1.21": +"@swc/types@^0.1.25": version "0.1.25" resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.25.tgz#b517b2a60feb37dd933e542d93093719e4cf1078" integrity sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g== @@ -4246,6 +4400,13 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node20/-/node20-20.1.6.tgz#cdf11db8322e1c245d5a4bb2e398239c82ae78b2" integrity sha512-sz+Hqx9zwZDpZIV871WSbUzSqNIsXzghZydypnfgzPKLltVJfkINfUeTct31n/tTSa9ZE1ZOfKdRre1uHHquYQ== +"@tybys/wasm-util@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414" + integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== + dependencies: + tslib "^2.4.0" + "@tybys/wasm-util@^0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.9.0.tgz#3e75eb00604c8d6db470bf18c37b7d984a0e3355" @@ -7597,7 +7758,7 @@ colorette@^1.2.1: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== -colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.16: +colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.16, colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -8446,6 +8607,13 @@ debug@^3.1.0, debug@^3.1.1, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -16055,6 +16223,31 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" +oxc-resolver@^11.6.1: + version "11.14.0" + resolved "https://registry.yarnpkg.com/oxc-resolver/-/oxc-resolver-11.14.0.tgz#8737deba9b8cd51054d56fb9886213d1fa7cf5a5" + integrity sha512-i4wNrqhOd+4YdHJfHglHtFiqqSxXuzFA+RUqmmWN1aMD3r1HqUSrIhw17tSO4jwKfhLs9uw1wzFPmvMsWacStg== + optionalDependencies: + "@oxc-resolver/binding-android-arm-eabi" "11.14.0" + "@oxc-resolver/binding-android-arm64" "11.14.0" + "@oxc-resolver/binding-darwin-arm64" "11.14.0" + "@oxc-resolver/binding-darwin-x64" "11.14.0" + "@oxc-resolver/binding-freebsd-x64" "11.14.0" + "@oxc-resolver/binding-linux-arm-gnueabihf" "11.14.0" + "@oxc-resolver/binding-linux-arm-musleabihf" "11.14.0" + "@oxc-resolver/binding-linux-arm64-gnu" "11.14.0" + "@oxc-resolver/binding-linux-arm64-musl" "11.14.0" + "@oxc-resolver/binding-linux-ppc64-gnu" "11.14.0" + "@oxc-resolver/binding-linux-riscv64-gnu" "11.14.0" + "@oxc-resolver/binding-linux-riscv64-musl" "11.14.0" + "@oxc-resolver/binding-linux-s390x-gnu" "11.14.0" + "@oxc-resolver/binding-linux-x64-gnu" "11.14.0" + "@oxc-resolver/binding-linux-x64-musl" "11.14.0" + "@oxc-resolver/binding-wasm32-wasi" "11.14.0" + "@oxc-resolver/binding-win32-arm64-msvc" "11.14.0" + "@oxc-resolver/binding-win32-ia32-msvc" "11.14.0" + "@oxc-resolver/binding-win32-x64-msvc" "11.14.0" + p-cancelable@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" @@ -16577,6 +16770,11 @@ pirates@^4.0.4, pirates@^4.0.6: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== +pirates@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" + integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== + piscina@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/piscina/-/piscina-3.2.0.tgz#f5a1dde0c05567775690cccefe59d9223924d154" @@ -18551,7 +18749,7 @@ source-map-support@0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.16, source-map-support@~0.5.12, source-map-support@~0.5.20: +source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@~0.5.12, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== From 3b2ed1373513c2691f14304e8fd5d7ca0731e512 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 11:48:57 -0800 Subject: [PATCH 40/69] yarn change --- ...ct-components-a2e03411-e541-4b7c-b342-5ebe7b8cd7ac.json | 7 +++++++ ...type-icons-v8-d71e68d3-89bb-4f77-ab67-c4051a45cac2.json | 7 +++++++ ...ared-contexts-acf99833-5132-498d-9ef1-abcc8ee5a656.json | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 change/@fluentui-react-components-a2e03411-e541-4b7c-b342-5ebe7b8cd7ac.json create mode 100644 change/@fluentui-react-file-type-icons-v8-d71e68d3-89bb-4f77-ab67-c4051a45cac2.json create mode 100644 change/@fluentui-react-shared-contexts-acf99833-5132-498d-9ef1-abcc8ee5a656.json diff --git a/change/@fluentui-react-components-a2e03411-e541-4b7c-b342-5ebe7b8cd7ac.json b/change/@fluentui-react-components-a2e03411-e541-4b7c-b342-5ebe7b8cd7ac.json new file mode 100644 index 00000000000000..49cf230d6548f9 --- /dev/null +++ b/change/@fluentui-react-components-a2e03411-e541-4b7c-b342-5ebe7b8cd7ac.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Adding react-file-type-icons package for FluentUI v9", + "packageName": "@fluentui/react-components", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-file-type-icons-v8-d71e68d3-89bb-4f77-ab67-c4051a45cac2.json b/change/@fluentui-react-file-type-icons-v8-d71e68d3-89bb-4f77-ab67-c4051a45cac2.json new file mode 100644 index 00000000000000..7a7a3b78a6e49b --- /dev/null +++ b/change/@fluentui-react-file-type-icons-v8-d71e68d3-89bb-4f77-ab67-c4051a45cac2.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Adding react-file-type-icons package for FluentUI v9. This package is now suffixed with v8 in the repo, but published to the same @fluentui/react-file-type-icons namespace in NPM with no suffix, just retaining the 8.x.x semver.", + "packageName": "@fluentui/react-file-type-icons-v8", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-shared-contexts-acf99833-5132-498d-9ef1-abcc8ee5a656.json b/change/@fluentui-react-shared-contexts-acf99833-5132-498d-9ef1-abcc8ee5a656.json new file mode 100644 index 00000000000000..f37e335fe31030 --- /dev/null +++ b/change/@fluentui-react-shared-contexts-acf99833-5132-498d-9ef1-abcc8ee5a656.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Adding react-file-type-icons package for FluentUI v9", + "packageName": "@fluentui/react-shared-contexts", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} From 07d1b03d3c90635df22d7f5c5b879a60324720d6 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 12:08:15 -0800 Subject: [PATCH 41/69] remove private flag from package.json for react-file-type-icons --- .../react-components/react-file-type-icons/library/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-components/react-file-type-icons/library/package.json b/packages/react-components/react-file-type-icons/library/package.json index 99660817316e34..e7363b155f2426 100644 --- a/packages/react-components/react-file-type-icons/library/package.json +++ b/packages/react-components/react-file-type-icons/library/package.json @@ -1,7 +1,6 @@ { "name": "@fluentui/react-file-type-icons", "version": "9.0.0", - "private": true, "description": "Filetype icons for FluentUI", "main": "lib-commonjs/index.js", "module": "lib/index.js", From 379416972453207c2cde6a6346ec528820df4172 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 12:23:49 -0800 Subject: [PATCH 42/69] deduplicating yarn lock and solving plugin issue in react-utilities .swcrc --- .../react-components/react-utilities/.swcrc | 3 - yarn.lock | 112 ++++++------------ 2 files changed, 39 insertions(+), 76 deletions(-) diff --git a/packages/react-components/react-utilities/.swcrc b/packages/react-components/react-utilities/.swcrc index 7c60535c03e245..b4ffa86dee3067 100644 --- a/packages/react-components/react-utilities/.swcrc +++ b/packages/react-components/react-utilities/.swcrc @@ -10,9 +10,6 @@ "/**/*.test.tsx" ], "jsc": { - "experimental": { - "plugins": [["swc-plugin-de-indent-template-literal", {}]] - }, "parser": { "syntax": "typescript", "tsx": true, diff --git a/yarn.lock b/yarn.lock index 1afa02e48e7ed6..a38d2c801de707 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1581,15 +1581,7 @@ "@effect/io" "^0.26.0" fast-check "^3.10.0" -"@emnapi/core@^1.1.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.4.0.tgz#8844b02d799198158ac1fea21ae2bc81b881da9a" - integrity sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg== - dependencies: - "@emnapi/wasi-threads" "1.0.1" - tslib "^2.4.0" - -"@emnapi/core@^1.5.0": +"@emnapi/core@^1.1.0", "@emnapi/core@^1.5.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.7.1.tgz#3a79a02dbc84f45884a1806ebb98e5746bdfaac4" integrity sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg== @@ -1597,27 +1589,13 @@ "@emnapi/wasi-threads" "1.1.0" tslib "^2.4.0" -"@emnapi/runtime@^1.1.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.4.0.tgz#8f509bf1059a5551c8fe829a1c4e91db35fdfbee" - integrity sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw== - dependencies: - tslib "^2.4.0" - -"@emnapi/runtime@^1.5.0": +"@emnapi/runtime@^1.1.0", "@emnapi/runtime@^1.5.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.7.1.tgz#a73784e23f5d57287369c808197288b52276b791" integrity sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA== dependencies: tslib "^2.4.0" -"@emnapi/wasi-threads@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz#d7ae71fd2166b1c916c6cd2d0df2ef565a2e1a5b" - integrity sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw== - dependencies: - tslib "^2.4.0" - "@emnapi/wasi-threads@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf" @@ -8579,10 +8557,10 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@^4.3.7: - version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@^4.3.7, debug@^4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" @@ -8607,13 +8585,6 @@ debug@^3.1.0, debug@^3.1.1, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.4.1: - version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -11331,17 +11302,17 @@ globalthis@^1.0.4: define-properties "^1.2.1" gopd "^1.0.1" -globby@*, globby@11.1.0, globby@^11.0.0, globby@^11.0.1, globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== +globby@*, globby@^14.0.0: + version "14.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.1.tgz#a1b44841aa7f4c6d8af2bc39951109d77301959b" + integrity sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ== dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.2" + ignore "^5.2.4" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" globby@11.0.4: version "11.0.4" @@ -11355,6 +11326,18 @@ globby@11.0.4: merge2 "^1.3.0" slash "^3.0.0" +globby@11.1.0, globby@^11.0.0, globby@^11.0.1, globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + globby@^10.0.1: version "10.0.2" resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" @@ -11369,18 +11352,6 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" -globby@^14.0.0: - version "14.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.1.tgz#a1b44841aa7f4c6d8af2bc39951109d77301959b" - integrity sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ== - dependencies: - "@sindresorhus/merge-streams" "^2.1.0" - fast-glob "^3.3.2" - ignore "^5.2.4" - path-type "^5.0.0" - slash "^5.1.0" - unicorn-magic "^0.1.0" - globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -16765,12 +16736,7 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pirates@^4.0.4, pirates@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" - integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== - -pirates@^4.0.7: +pirates@^4.0.4, pirates@^4.0.6, pirates@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== @@ -18142,20 +18108,20 @@ rw@1: resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== -rxjs@>=6.4.0, rxjs@^6.5.5, rxjs@^6.6.0: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -rxjs@^7.5.1: +rxjs@>=6.4.0, rxjs@^7.5.1: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" +rxjs@^6.5.5, rxjs@^6.6.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + sade@^1.7.3: version "1.8.1" resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" @@ -18179,12 +18145,12 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@5.1.2, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== From 935f9679e3dc058c0b5e826f7d727f0ca3b627be Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 12:31:38 -0800 Subject: [PATCH 43/69] yarn change for react-utilities --- ...act-utilities-16ca2224-8177-4ede-ba6a-91d9c817054f.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-utilities-16ca2224-8177-4ede-ba6a-91d9c817054f.json diff --git a/change/@fluentui-react-utilities-16ca2224-8177-4ede-ba6a-91d9c817054f.json b/change/@fluentui-react-utilities-16ca2224-8177-4ede-ba6a-91d9c817054f.json new file mode 100644 index 00000000000000..953eb2d68a01d6 --- /dev/null +++ b/change/@fluentui-react-utilities-16ca2224-8177-4ede-ba6a-91d9c817054f.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Adding react-file-type-icons package for FluentUI v9", + "packageName": "@fluentui/react-utilities", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} From 237b828c0d7d4582abc73436acd20cb9c8a1b00a Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 12:39:20 -0800 Subject: [PATCH 44/69] npx yarn-deduplicate --strategy fewer --- yarn.lock | 64 +++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/yarn.lock b/yarn.lock index a38d2c801de707..b6a6323af9ef4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11302,17 +11302,17 @@ globalthis@^1.0.4: define-properties "^1.2.1" gopd "^1.0.1" -globby@*, globby@^14.0.0: - version "14.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.1.tgz#a1b44841aa7f4c6d8af2bc39951109d77301959b" - integrity sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ== +globby@*, globby@11.1.0, globby@^11.0.0, globby@^11.0.1, globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: - "@sindresorhus/merge-streams" "^2.1.0" - fast-glob "^3.3.2" - ignore "^5.2.4" - path-type "^5.0.0" - slash "^5.1.0" - unicorn-magic "^0.1.0" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" globby@11.0.4: version "11.0.4" @@ -11326,18 +11326,6 @@ globby@11.0.4: merge2 "^1.3.0" slash "^3.0.0" -globby@11.1.0, globby@^11.0.0, globby@^11.0.1, globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - globby@^10.0.1: version "10.0.2" resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" @@ -11352,6 +11340,18 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" +globby@^14.0.0: + version "14.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.1.tgz#a1b44841aa7f4c6d8af2bc39951109d77301959b" + integrity sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ== + dependencies: + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.2" + ignore "^5.2.4" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" + globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -18108,20 +18108,20 @@ rw@1: resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== -rxjs@>=6.4.0, rxjs@^7.5.1: - version "7.8.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" - integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== - dependencies: - tslib "^2.1.0" - -rxjs@^6.5.5, rxjs@^6.6.0: +rxjs@>=6.4.0, rxjs@^6.5.5, rxjs@^6.6.0: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" +rxjs@^7.5.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + sade@^1.7.3: version "1.8.1" resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" @@ -18145,12 +18145,12 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2: +safe-buffer@5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== From cb42047f17e663e8e9905e5a8670c42a7a8c3afe Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 12:57:21 -0800 Subject: [PATCH 45/69] Add path mapping for react-file-type-icons-v8 in tsconfig.base.all.json --- .../src/generators/tsconfig-base-all/lib/utils.ts | 5 ++--- tsconfig.base.all.json | 11 ++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/workspace-plugin/src/generators/tsconfig-base-all/lib/utils.ts b/tools/workspace-plugin/src/generators/tsconfig-base-all/lib/utils.ts index 9bc9c3e0730a39..62382e3da8517b 100644 --- a/tools/workspace-plugin/src/generators/tsconfig-base-all/lib/utils.ts +++ b/tools/workspace-plugin/src/generators/tsconfig-base-all/lib/utils.ts @@ -1,5 +1,4 @@ // use this module to define any kind of generic utilities that are used in more than 1 place within the generator implementation -import path from 'path'; import { readJson, Tree } from '@nx/devkit'; /** @@ -11,8 +10,8 @@ export function createPathAliasesConfig(tree: Tree) { const existingTsConfig = tree.exists(tsConfigAllPath) ? readJson(tree, tsConfigAllPath) : null; const baseConfigs = { - v8: readJson(tree, path.join('/tsconfig.base.v8.json')), - v9: readJson(tree, path.join('/tsconfig.base.json')), + v8: readJson(tree, '/tsconfig.base.v8.json'), + v9: readJson(tree, '/tsconfig.base.json'), }; const tsConfigBase = '.'; const mergedTsConfig = { diff --git a/tsconfig.base.all.json b/tsconfig.base.all.json index fa01a90c54fe9b..d4aa1e0359ef04 100644 --- a/tsconfig.base.all.json +++ b/tsconfig.base.all.json @@ -26,6 +26,7 @@ "@fluentui/react-date-time": ["packages/react-date-time/src/index.ts"], "@fluentui/react-experiments": ["packages/react-experiments/src/index.ts"], "@fluentui/react-experiments/lib/*": ["packages/react-experiments/src/*"], + "@fluentui/react-file-type-icons-v8": ["packages/react-file-type-icons/src/index.ts"], "@fluentui/react-focus": ["packages/react-focus/src/index.ts"], "@fluentui/react-hooks": ["packages/react-hooks/src/index.ts"], "@fluentui/scheme-utilities": ["packages/scheme-utilities/src/index.ts"], @@ -70,7 +71,6 @@ "@fluentui/eslint-plugin-react-components": [ "packages/react-components/eslint-plugin-react-components/src/index.ts" ], - "@fluentui/react-file-type-icons": ["packages/react-components/react-file-type-icons/library/src/index.ts"], "@fluentui/global-context": ["packages/react-components/global-context/src/index.ts"], "@fluentui/keyboard-key": ["packages/keyboard-key/src/index.ts"], "@fluentui/keyboard-keys": ["packages/react-components/keyboard-keys/src/index.ts"], @@ -121,6 +121,10 @@ "@fluentui/react-drawer-stories": ["packages/react-components/react-drawer/stories/src/index.ts"], "@fluentui/react-field": ["packages/react-components/react-field/library/src/index.ts"], "@fluentui/react-field-stories": ["packages/react-components/react-field/stories/src/index.ts"], + "@fluentui/react-file-type-icons": ["packages/react-components/react-file-type-icons/library/src/index.ts"], + "@fluentui/react-file-type-icons-stories": [ + "packages/react-components/react-file-type-icons/stories/src/index.ts" + ], "@fluentui/react-focus-management": ["packages/react-focus-management/src/index.ts"], "@fluentui/react-icons-compat": ["packages/react-components/react-icons-compat/library/src/index.ts"], "@fluentui/react-icons-compat-stories": ["packages/react-components/react-icons-compat/stories/src/index.ts"], @@ -254,10 +258,7 @@ "@fluentui/tokens": ["packages/tokens/src/index.ts"], "@fluentui/visual-regression-assert": ["tools/visual-regression-assert/src/index.ts"], "@fluentui/visual-regression-utilities": ["tools/visual-regression-utilities/src/index.ts"], - "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"], - "@fluentui/react-file-type-icons-stories": [ - "packages/react-components/react-file-type-icons/stories/src/index.ts" - ] + "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"] } } } From cfa9fba0fd5297719016716a1068f4c566fdc108 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 13:10:37 -0800 Subject: [PATCH 46/69] re-running yarn nx format:write --- .../src/components/FileTypeIcon/useFileTypeIcon.ts | 8 +++++++- .../library/src/utils/getFileTypeIconAsUrl.test.ts | 12 +++++++++--- .../FileTypeIcon/FileTypeIconEdgeCases.stories.tsx | 6 +++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts index 316cbb0067075c..abd7aeb7808215 100644 --- a/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts +++ b/packages/react-components/react-file-type-icons/library/src/components/FileTypeIcon/useFileTypeIcon.ts @@ -17,7 +17,13 @@ export const useFileTypeIcon_unstable = ( props: FileTypeIconProps, ref: React.Ref, ): FileTypeIconState => { - const { extension, type, size: requestedSize = DEFAULT_ICON_SIZE, imageFileType = 'svg', baseUrl = getConfiguredBaseUrl() } = props; + const { + extension, + type, + size: requestedSize = DEFAULT_ICON_SIZE, + imageFileType = 'svg', + baseUrl = getConfiguredBaseUrl(), + } = props; // Validate and adjust size to nearest available const size = getValidIconSize(requestedSize); diff --git a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts index b83ea3c4630d15..35f7f0d77b6de9 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.test.ts @@ -351,7 +351,9 @@ describe('getFileTypeIconAsHTMLString', () => { { extension: 'pdf', size: 16, imageFileType: 'png' }, DEFAULT_BASE_URL, ); - expect(result).toBe(`pdf file icon`); + expect(result).toBe( + `pdf file icon`, + ); }); it('should return correct HTML string for 2x DPI with xlsx extension', () => { @@ -365,7 +367,9 @@ describe('getFileTypeIconAsHTMLString', () => { { extension: 'xlsx', size: 16, imageFileType: 'png' }, DEFAULT_BASE_URL, ); - expect(result).toBe(`xlsx file icon`); + expect(result).toBe( + `xlsx file icon`, + ); }); }); @@ -389,7 +393,9 @@ describe('getFileTypeIconAsHTMLString', () => { { extension: 'pdf', size: 20, imageFileType: 'svg' }, DEFAULT_BASE_URL, ); - expect(result).toBe(`pdf file icon`); + expect(result).toBe( + `pdf file icon`, + ); }); }); diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx index 35fc6816866298..ae00b2a590f73f 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx @@ -215,8 +215,9 @@ export const EdgeCases = (): JSXElement => {
.Pdf
-
All variations display the same PDF icon. If you want to further sanitize - your code, consider extracting and normalizing file extensions from full filenames: +
+ All variations display the same PDF icon. If you want to further sanitize your code, consider extracting and + normalizing file extensions from full filenames:
           
@@ -234,7 +235,6 @@ const extension = getExtension(filename); // "pdf"
           
         
-
); }; From ef929f168b6152f201a3156e45c69efd008d16cd Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 13:41:38 -0800 Subject: [PATCH 47/69] undoing removal of indent rule --- packages/react-components/react-utilities/.swcrc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-components/react-utilities/.swcrc b/packages/react-components/react-utilities/.swcrc index b4ffa86dee3067..7c60535c03e245 100644 --- a/packages/react-components/react-utilities/.swcrc +++ b/packages/react-components/react-utilities/.swcrc @@ -10,6 +10,9 @@ "/**/*.test.tsx" ], "jsc": { + "experimental": { + "plugins": [["swc-plugin-de-indent-template-literal", {}]] + }, "parser": { "syntax": "typescript", "tsx": true, From 9824a02704fbd55e5634c47059330f23aa3abd28 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 13:58:52 -0800 Subject: [PATCH 48/69] plugin swc-plugin-de-indent-template-literal is crashing, removing --- packages/react-components/react-dialog/library/.swcrc | 3 --- packages/react-components/react-jsx-runtime/.swcrc | 3 --- packages/react-components/react-tree/library/.swcrc | 3 --- packages/react-components/react-utilities/.swcrc | 5 +---- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/react-components/react-dialog/library/.swcrc b/packages/react-components/react-dialog/library/.swcrc index 7c60535c03e245..b4ffa86dee3067 100644 --- a/packages/react-components/react-dialog/library/.swcrc +++ b/packages/react-components/react-dialog/library/.swcrc @@ -10,9 +10,6 @@ "/**/*.test.tsx" ], "jsc": { - "experimental": { - "plugins": [["swc-plugin-de-indent-template-literal", {}]] - }, "parser": { "syntax": "typescript", "tsx": true, diff --git a/packages/react-components/react-jsx-runtime/.swcrc b/packages/react-components/react-jsx-runtime/.swcrc index 7c60535c03e245..b4ffa86dee3067 100644 --- a/packages/react-components/react-jsx-runtime/.swcrc +++ b/packages/react-components/react-jsx-runtime/.swcrc @@ -10,9 +10,6 @@ "/**/*.test.tsx" ], "jsc": { - "experimental": { - "plugins": [["swc-plugin-de-indent-template-literal", {}]] - }, "parser": { "syntax": "typescript", "tsx": true, diff --git a/packages/react-components/react-tree/library/.swcrc b/packages/react-components/react-tree/library/.swcrc index 7c60535c03e245..b4ffa86dee3067 100644 --- a/packages/react-components/react-tree/library/.swcrc +++ b/packages/react-components/react-tree/library/.swcrc @@ -10,9 +10,6 @@ "/**/*.test.tsx" ], "jsc": { - "experimental": { - "plugins": [["swc-plugin-de-indent-template-literal", {}]] - }, "parser": { "syntax": "typescript", "tsx": true, diff --git a/packages/react-components/react-utilities/.swcrc b/packages/react-components/react-utilities/.swcrc index 7c60535c03e245..1f90e743a01739 100644 --- a/packages/react-components/react-utilities/.swcrc +++ b/packages/react-components/react-utilities/.swcrc @@ -1,5 +1,5 @@ { - "$schema": "https://json.schemastore.org/swcrc", + "$schema": "https://swc.rs/schema.json", "exclude": [ "/testing", "/**/*.cy.ts", @@ -10,9 +10,6 @@ "/**/*.test.tsx" ], "jsc": { - "experimental": { - "plugins": [["swc-plugin-de-indent-template-literal", {}]] - }, "parser": { "syntax": "typescript", "tsx": true, From 9b0917c445be9d20ea824e33561aeccf51d67a72 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 14:00:37 -0800 Subject: [PATCH 49/69] yarn change for experimental swc.rs indentation plugin removal --- ...-react-dialog-8feae847-a6df-438f-babf-60e6ecb351c5.json | 7 +++++++ ...t-jsx-runtime-7b4bdb9f-c16e-4bda-9459-b3b7c30755ab.json | 7 +++++++ ...ui-react-tree-c84f263a-964a-47d9-9a5c-337406e22fa5.json | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 change/@fluentui-react-dialog-8feae847-a6df-438f-babf-60e6ecb351c5.json create mode 100644 change/@fluentui-react-jsx-runtime-7b4bdb9f-c16e-4bda-9459-b3b7c30755ab.json create mode 100644 change/@fluentui-react-tree-c84f263a-964a-47d9-9a5c-337406e22fa5.json diff --git a/change/@fluentui-react-dialog-8feae847-a6df-438f-babf-60e6ecb351c5.json b/change/@fluentui-react-dialog-8feae847-a6df-438f-babf-60e6ecb351c5.json new file mode 100644 index 00000000000000..5fa0fefdde37fd --- /dev/null +++ b/change/@fluentui-react-dialog-8feae847-a6df-438f-babf-60e6ecb351c5.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "plugin swc-plugin-de-indent-template-literal is crashing, removing", + "packageName": "@fluentui/react-dialog", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-jsx-runtime-7b4bdb9f-c16e-4bda-9459-b3b7c30755ab.json b/change/@fluentui-react-jsx-runtime-7b4bdb9f-c16e-4bda-9459-b3b7c30755ab.json new file mode 100644 index 00000000000000..cb9eaed66d38ea --- /dev/null +++ b/change/@fluentui-react-jsx-runtime-7b4bdb9f-c16e-4bda-9459-b3b7c30755ab.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "plugin swc-plugin-de-indent-template-literal is crashing, removing", + "packageName": "@fluentui/react-jsx-runtime", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-tree-c84f263a-964a-47d9-9a5c-337406e22fa5.json b/change/@fluentui-react-tree-c84f263a-964a-47d9-9a5c-337406e22fa5.json new file mode 100644 index 00000000000000..020e81b21ce7e7 --- /dev/null +++ b/change/@fluentui-react-tree-c84f263a-964a-47d9-9a5c-337406e22fa5.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "plugin swc-plugin-de-indent-template-literal is crashing, removing", + "packageName": "@fluentui/react-tree", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} From 2d73b85095c3aa743f5d47ac6f6efcb2c65d0403 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 14:14:03 -0800 Subject: [PATCH 50/69] fix missing param in documentation for getFileTypeIcon --- .../library/src/utils/getFileTypeIconAsUrl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts index 88ee15e35535bc..d27885a86ef5d0 100644 --- a/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts +++ b/packages/react-components/react-file-type-icons/library/src/utils/getFileTypeIconAsUrl.ts @@ -10,7 +10,7 @@ import { FileIconType } from './FileIconType'; /** * Given the `fileTypeIconOptions`, this function returns the CDN-based URL for `FileTypeIcon`. * Similar to `getFileTypeIconProps`, but rather than returning the `iconName`, this returns the raw URL. - * @param options + * @param options - Provide extension, FileIconType, size, and imageFileType for the requested icon. * @param baseUrl - optionally provide a custom CDN base url to fetch icons from. * If not provided, uses the URL configured via `initializeFileTypeIcons()`, * or falls back to the default CDN URL. @@ -37,7 +37,7 @@ export function getFileTypeIconAsUrl( /** * Given the `fileTypeIconOptions`, similar to `getFileTypeIconProps`, this function returns * an tag DOM element that renders the icon, as a string. - * @param options + * @param options - Provide extension, FileIconType, size, and imageFileType for the requested icon. * @param baseUrl - optionally provide a custom CDN base url to fetch icons from. * If not provided, uses the URL configured via `initializeFileTypeIcons()`, * or falls back to the default CDN URL. From 13974b76dc7674fc68c7f7806f62492ba1a8a703 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 16:49:23 -0800 Subject: [PATCH 51/69] Add missing dependency and update styles in file type icon stories --- .../stories/package.json | 1 + .../FileTypeIconCommon.stories.tsx | 8 +- .../FileTypeIconDefault.stories.tsx | 4 +- .../FileTypeIconEdgeCases.stories.tsx | 18 +- .../FileTypeIconUrlAndHtml.stories.tsx | 274 +++++++++--------- .../src/FileTypeIcon/index.stories.tsx | 3 +- .../tsconfig.lib.json | 1 + .../storybook-llms-extractor/src/typings.d.ts | 7 - .../tsconfig.lib.json | 1 + .../tsconfig.lib.json | 1 + 10 files changed, 159 insertions(+), 159 deletions(-) delete mode 100644 tools/storybook-llms-extractor/src/typings.d.ts diff --git a/packages/react-components/react-file-type-icons/stories/package.json b/packages/react-components/react-file-type-icons/stories/package.json index 75570f7e81e644..880429588b2e1c 100644 --- a/packages/react-components/react-file-type-icons/stories/package.json +++ b/packages/react-components/react-file-type-icons/stories/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "devDependencies": { + "@fluentui/react-components": "*", "@fluentui/react-file-type-icons": "*", "@fluentui/react-storybook-addon": "*", "@fluentui/react-storybook-addon-export-to-sandbox": "*", diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx index 4449b2c8568af6..4acc77589980be 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconCommon.stories.tsx @@ -1,8 +1,8 @@ +/* eslint-disable @fluentui/no-restricted-imports */ import * as React from 'react'; +import { makeStyles, tokens } from '@fluentui/react-components'; import type { JSXElement } from '@fluentui/react-components'; -import { tokens } from '@fluentui/react-components'; import { FileTypeIcon, FileIconType } from '@fluentui/react-file-type-icons'; -import { makeStyles, shorthands } from '@griffel/react'; const useStyles = makeStyles({ container: { @@ -30,9 +30,9 @@ const useStyles = makeStyles({ flexDirection: 'column', alignItems: 'center', gap: '8px', - ...shorthands.padding('12px'), + padding: '12px', backgroundColor: tokens.colorNeutralBackground1, - ...shorthands.borderRadius(tokens.borderRadiusMedium), + borderRadius: tokens.borderRadiusMedium, ':hover': { backgroundColor: tokens.colorNeutralBackground1Hover, }, diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx index db8cda511a7f51..8f150560d7b118 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconDefault.stories.tsx @@ -1,8 +1,8 @@ +/* eslint-disable @fluentui/no-restricted-imports */ import * as React from 'react'; +import { makeStyles, tokens } from '@fluentui/react-components'; import type { JSXElement } from '@fluentui/react-components'; -import { tokens } from '@fluentui/react-components'; import { FileIconType, FileTypeIcon } from '@fluentui/react-file-type-icons'; -import { makeStyles } from '@griffel/react'; const useStyles = makeStyles({ container: { diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx index ae00b2a590f73f..e5e2e61ffb3a36 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconEdgeCases.stories.tsx @@ -1,8 +1,8 @@ +/* eslint-disable @fluentui/no-restricted-imports */ import * as React from 'react'; +import { makeStyles, tokens } from '@fluentui/react-components'; import type { JSXElement } from '@fluentui/react-components'; -import { tokens } from '@fluentui/react-components'; import { FileTypeIcon } from '@fluentui/react-file-type-icons'; -import { makeStyles, shorthands } from '@griffel/react'; const useStyles = makeStyles({ container: { @@ -34,9 +34,9 @@ const useStyles = makeStyles({ display: 'flex', alignItems: 'center', gap: '12px', - ...shorthands.padding('12px'), + padding: '12px', backgroundColor: tokens.colorNeutralBackground1, - ...shorthands.borderRadius(tokens.borderRadiusMedium), + borderRadius: tokens.borderRadiusMedium, }, label: { fontSize: tokens.fontSizeBase300, @@ -48,16 +48,16 @@ const useStyles = makeStyles({ fontStyle: 'italic', }, warningBox: { - ...shorthands.padding('12px', '16px'), + padding: '12px 16px', backgroundColor: tokens.colorPaletteYellowBackground2, - ...shorthands.borderLeft('4px', 'solid', tokens.colorPaletteYellowBorder1), - ...shorthands.borderRadius(tokens.borderRadiusMedium), + borderLeft: `4px solid ${tokens.colorPaletteYellowBorder1}`, + borderRadius: tokens.borderRadiusMedium, fontSize: tokens.fontSizeBase300, }, codeBlock: { backgroundColor: tokens.colorNeutralBackground3, - ...shorthands.padding('12px'), - ...shorthands.borderRadius(tokens.borderRadiusMedium), + padding: '12px', + borderRadius: tokens.borderRadiusMedium, fontFamily: tokens.fontFamilyMonospace, fontSize: tokens.fontSizeBase200, overflowX: 'auto', diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx index 64f5bd35d3294b..0b6555fb02543e 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx @@ -1,8 +1,8 @@ +/* eslint-disable @fluentui/no-restricted-imports */ import * as React from 'react'; +import { makeStyles, tokens } from '@fluentui/react-components'; import type { JSXElement } from '@fluentui/react-components'; -import { tokens } from '@fluentui/react-components'; import { getFileTypeIconAsUrl, getFileTypeIconAsHTMLString } from '@fluentui/react-file-type-icons'; -import { makeStyles, shorthands } from '@griffel/react'; const useStyles = makeStyles({ container: { @@ -14,9 +14,9 @@ const useStyles = makeStyles({ display: 'flex', flexDirection: 'column', gap: '12px', - ...shorthands.padding('16px'), - ...shorthands.border('1px', 'solid', tokens.colorNeutralStroke1), - ...shorthands.borderRadius(tokens.borderRadiusMedium), + padding: '16px', + border: `1px solid ${tokens.colorNeutralStroke1}`, + borderRadius: tokens.borderRadiusMedium, }, sectionTitle: { fontSize: tokens.fontSizeBase400, @@ -32,9 +32,9 @@ const useStyles = makeStyles({ display: 'flex', flexDirection: 'column', gap: '8px', - ...shorthands.padding('12px'), + padding: '12px', backgroundColor: tokens.colorNeutralBackground2, - ...shorthands.borderRadius(tokens.borderRadiusSmall), + borderRadius: tokens.borderRadiusSmall, }, cardTitle: { fontSize: tokens.fontSizeBase300, @@ -45,17 +45,17 @@ const useStyles = makeStyles({ display: 'flex', alignItems: 'center', justifyContent: 'center', - ...shorthands.padding('12px'), + padding: '12px', backgroundColor: tokens.colorNeutralBackground1, - ...shorthands.borderRadius(tokens.borderRadiusSmall), + borderRadius: tokens.borderRadiusSmall, minHeight: '60px', }, code: { fontSize: tokens.fontSizeBase100, fontFamily: tokens.fontFamilyMonospace, backgroundColor: tokens.colorNeutralBackground1, - ...shorthands.padding('8px'), - ...shorthands.borderRadius(tokens.borderRadiusSmall), + padding: '8px', + borderRadius: tokens.borderRadiusSmall, overflowX: 'auto', wordBreak: 'break-all', }, @@ -68,148 +68,150 @@ const useStyles = makeStyles({ const commonFileTypes = ['docx', 'pdf', 'xlsx']; -export const UrlAndHtml = { - render: (): JSXElement => { - const styles = useStyles(); +const UrlAndHtmlComponent = (): JSXElement => { + const styles = useStyles(); - return ( -
- {/* URL Function Demo */} -
-
getFileTypeIconAsUrl() - PNG Format with Different DPIs
-
- {commonFileTypes.map(extension => ( -
-
.{extension}
- {(['1x', '1.5x', '2x'] as const).map(dpi => { - const url = getFileTypeIconAsUrl({ - extension, - size: 48, - imageFileType: 'png', - }); + return ( +
+ {/* URL Function Demo */} +
+
getFileTypeIconAsUrl() - PNG Format with Different DPIs
+
+ {commonFileTypes.map(extension => ( +
+
.{extension}
+ {(['1x', '1.5x', '2x'] as const).map(dpi => { + const url = getFileTypeIconAsUrl({ + extension, + size: 48, + imageFileType: 'png', + }); - // For demo purposes, we'll show what the URL would be for each DPI - // In real usage, the browser's devicePixelRatio would determine this - const dpiSuffix = dpi === '1x' ? '' : `_${dpi}`; - const demoUrl = url?.replace(/48/, `48${dpiSuffix}`); + // For demo purposes, we'll show what the URL would be for each DPI + // In real usage, the browser's devicePixelRatio would determine this + const dpiSuffix = dpi === '1x' ? '' : `_${dpi}`; + const demoUrl = url?.replace(/48/, `48${dpiSuffix}`); - return ( -
-
{dpi} DPI:
-
{demoUrl}
-
- ); - })} -
- ))} -
+ return ( +
+
{dpi} DPI:
+
{demoUrl}
+
+ ); + })} +
+ ))}
+
- {/* SVG URL Demo */} -
-
getFileTypeIconAsUrl() - SVG Format with Different DPIs
-
- {commonFileTypes.map(extension => ( -
-
.{extension}
- {(['1x', '1.5x', '2x'] as const).map(dpi => { - const url = getFileTypeIconAsUrl({ - extension, - size: 48, - imageFileType: 'svg', - }); + {/* SVG URL Demo */} +
+
getFileTypeIconAsUrl() - SVG Format with Different DPIs
+
+ {commonFileTypes.map(extension => ( +
+
.{extension}
+ {(['1x', '1.5x', '2x'] as const).map(dpi => { + const url = getFileTypeIconAsUrl({ + extension, + size: 48, + imageFileType: 'svg', + }); - // SVG only uses 1.5x for specific DPI ranges, otherwise uses base - const dpiSuffix = dpi === '1.5x' ? '_1.5x' : ''; - const demoUrl = url?.replace(/48/, `48${dpiSuffix}`); + // SVG only uses 1.5x for specific DPI ranges, otherwise uses base + const dpiSuffix = dpi === '1.5x' ? '_1.5x' : ''; + const demoUrl = url?.replace(/48/, `48${dpiSuffix}`); - return ( -
-
{dpi} DPI:
-
{demoUrl}
-
- ); - })} -
- ))} -
+ return ( +
+
{dpi} DPI:
+
{demoUrl}
+
+ ); + })} +
+ ))}
+
- {/* HTML String Demo */} -
-
getFileTypeIconAsHTMLString() - Visual Preview
-
- {commonFileTypes.map(extension => ( -
-
.{extension}
-
-
-
-
HTML Output:
-
- {getFileTypeIconAsHTMLString({ - extension, - size: 48, - imageFileType: 'svg', - })} -
+ {/* HTML String Demo */} +
+
getFileTypeIconAsHTMLString() - Visual Preview
+
+ {commonFileTypes.map(extension => ( +
+
.{extension}
+
+
- ))} -
+
HTML Output:
+
+ {getFileTypeIconAsHTMLString({ + extension, + size: 48, + imageFileType: 'svg', + })} +
+
+ ))}
+
- {/* Comparison at Different Sizes */} -
-
getFileTypeIconAsHTMLString() - Different Sizes
-
- {([16, 24, 48] as const).map(size => ( -
-
Size: {size}px
- {commonFileTypes.map(extension => ( -
-
.{extension}:
-
-
-
+ {/* Comparison at Different Sizes */} +
+
getFileTypeIconAsHTMLString() - Different Sizes
+
+ {([16, 24, 48] as const).map(size => ( +
+
Size: {size}px
+ {commonFileTypes.map(extension => ( +
+
.{extension}:
+
+
- ))} -
- ))} -
+
+ ))} +
+ ))}
+
- {/* Interactive Example */} -
-
Current Device Information
-
- Device Pixel Ratio: {typeof window !== 'undefined' ? window.devicePixelRatio : 'N/A'} -
-
- This affects which icon variant is loaded for PNG images. SVG images scale better but may use 1.5x variant - for better rendering. -
+ {/* Interactive Example */} +
+
Current Device Information
+
+ Device Pixel Ratio: {typeof window !== 'undefined' ? window.devicePixelRatio : 'N/A'} +
+
+ This affects which icon variant is loaded for PNG images. SVG images scale better but may use 1.5x variant + for better rendering.
- ); - }, +
+ ); +}; + +export const UrlAndHtml = { + render: UrlAndHtmlComponent, name: 'URL and HTML Functions', parameters: { docs: { diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx index 06451131e0a04d..043aecf909fe74 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/index.stories.tsx @@ -1,5 +1,6 @@ -import { FileTypeIcon } from '@fluentui/react-file-type-icons'; +/* eslint-disable @fluentui/no-restricted-imports */ import type { Meta } from '@storybook/react'; +import { FileTypeIcon } from '@fluentui/react-file-type-icons'; import descriptionMd from './FileTypeIconDescription.md'; import bestPracticesMd from './FileTypeIconBestPractices.md'; diff --git a/tools/react-integration-tester/tsconfig.lib.json b/tools/react-integration-tester/tsconfig.lib.json index 33eca2c2cdf8c6..71bb06dd725935 100644 --- a/tools/react-integration-tester/tsconfig.lib.json +++ b/tools/react-integration-tester/tsconfig.lib.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "rootDir": "src", "outDir": "../../dist/out-tsc", "declaration": true, "types": ["node"] diff --git a/tools/storybook-llms-extractor/src/typings.d.ts b/tools/storybook-llms-extractor/src/typings.d.ts deleted file mode 100644 index 3e63e0662d2ff3..00000000000000 --- a/tools/storybook-llms-extractor/src/typings.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare module 'turndown-plugin-gfm' { - import type { Plugin } from 'turndown'; - - export const strikethrough: Plugin; - export const tables: Plugin; - export const taskListItems: Plugin; -} diff --git a/tools/storybook-llms-extractor/tsconfig.lib.json b/tools/storybook-llms-extractor/tsconfig.lib.json index 0e55672051597b..3bcfb1fccd2c99 100644 --- a/tools/storybook-llms-extractor/tsconfig.lib.json +++ b/tools/storybook-llms-extractor/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "module": "CommonJS", + "rootDir": "src", "noEmit": false, "lib": ["ES2020", "DOM"], "declaration": true, diff --git a/tools/visual-regression-assert/tsconfig.lib.json b/tools/visual-regression-assert/tsconfig.lib.json index e8e07a7a01fe7b..27ed0f811a1f10 100644 --- a/tools/visual-regression-assert/tsconfig.lib.json +++ b/tools/visual-regression-assert/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "module": "CommonJS", + "rootDir": "src", "noEmit": false, "lib": ["ES2020"], "declaration": true, From d949974c2b2848d2cb81a12cf9f8ce5645d2d414 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 16:56:22 -0800 Subject: [PATCH 52/69] lint and yarn change again --- ...lms-extractor-5bd0b738-e38e-4a5e-bc2c-7861db41e211.json | 7 +++++++ .../src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 change/@fluentui-storybook-llms-extractor-5bd0b738-e38e-4a5e-bc2c-7861db41e211.json diff --git a/change/@fluentui-storybook-llms-extractor-5bd0b738-e38e-4a5e-bc2c-7861db41e211.json b/change/@fluentui-storybook-llms-extractor-5bd0b738-e38e-4a5e-bc2c-7861db41e211.json new file mode 100644 index 00000000000000..d23f99308f1b95 --- /dev/null +++ b/change/@fluentui-storybook-llms-extractor-5bd0b738-e38e-4a5e-bc2c-7861db41e211.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "cleanup of stray .d.ts", + "packageName": "@fluentui/storybook-llms-extractor", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx index 0b6555fb02543e..901c595222c4a5 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx @@ -202,8 +202,8 @@ const UrlAndHtmlComponent = (): JSXElement => { Device Pixel Ratio: {typeof window !== 'undefined' ? window.devicePixelRatio : 'N/A'}
- This affects which icon variant is loaded for PNG images. SVG images scale better but may use 1.5x variant - for better rendering. + This affects which icon variant is loaded for PNG images. SVG images scale better but may use 1.5x variant for + better rendering.
From 2d6e940d7e79f05d1365cadab025b3a8aeeb8397 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 17:06:09 -0800 Subject: [PATCH 53/69] undoing overeager deletion of a file i had no business touching --- ...lms-extractor-5bd0b738-e38e-4a5e-bc2c-7861db41e211.json | 7 ------- tools/storybook-llms-extractor/src/typings.d.ts | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 change/@fluentui-storybook-llms-extractor-5bd0b738-e38e-4a5e-bc2c-7861db41e211.json create mode 100644 tools/storybook-llms-extractor/src/typings.d.ts diff --git a/change/@fluentui-storybook-llms-extractor-5bd0b738-e38e-4a5e-bc2c-7861db41e211.json b/change/@fluentui-storybook-llms-extractor-5bd0b738-e38e-4a5e-bc2c-7861db41e211.json deleted file mode 100644 index d23f99308f1b95..00000000000000 --- a/change/@fluentui-storybook-llms-extractor-5bd0b738-e38e-4a5e-bc2c-7861db41e211.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "patch", - "comment": "cleanup of stray .d.ts", - "packageName": "@fluentui/storybook-llms-extractor", - "email": "caperez@microsoft.com", - "dependentChangeType": "patch" -} diff --git a/tools/storybook-llms-extractor/src/typings.d.ts b/tools/storybook-llms-extractor/src/typings.d.ts new file mode 100644 index 00000000000000..3e63e0662d2ff3 --- /dev/null +++ b/tools/storybook-llms-extractor/src/typings.d.ts @@ -0,0 +1,7 @@ +declare module 'turndown-plugin-gfm' { + import type { Plugin } from 'turndown'; + + export const strikethrough: Plugin; + export const tables: Plugin; + export const taskListItems: Plugin; +} From ba6015cf72f8835f33e99028d757625477d8f9c9 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 17:10:16 -0800 Subject: [PATCH 54/69] fixing compilation config issues - no change --- ...lms-extractor-1e20ce18-54fc-412a-bcaa-65ecabde4e57.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-storybook-llms-extractor-1e20ce18-54fc-412a-bcaa-65ecabde4e57.json diff --git a/change/@fluentui-storybook-llms-extractor-1e20ce18-54fc-412a-bcaa-65ecabde4e57.json b/change/@fluentui-storybook-llms-extractor-1e20ce18-54fc-412a-bcaa-65ecabde4e57.json new file mode 100644 index 00000000000000..8f4167c2d18c7b --- /dev/null +++ b/change/@fluentui-storybook-llms-extractor-1e20ce18-54fc-412a-bcaa-65ecabde4e57.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "fixing compilation config issues", + "packageName": "@fluentui/storybook-llms-extractor", + "email": "caperez@microsoft.com", + "dependentChangeType": "none" +} From c2ad25aacad60a5ef4845efc9d74646b56ffe1d1 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 18:03:21 -0800 Subject: [PATCH 55/69] Refactor UrlAndHtml component export and update story parameters --- .../FileTypeIconUrlAndHtml.stories.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx index 901c595222c4a5..47b449e1e39170 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx @@ -68,7 +68,7 @@ const useStyles = makeStyles({ const commonFileTypes = ['docx', 'pdf', 'xlsx']; -const UrlAndHtmlComponent = (): JSXElement => { +export const UrlAndHtml = (): JSXElement => { const styles = useStyles(); return ( @@ -210,15 +210,13 @@ const UrlAndHtmlComponent = (): JSXElement => { ); }; -export const UrlAndHtml = { - render: UrlAndHtmlComponent, - name: 'URL and HTML Functions', - parameters: { - docs: { - description: { - story: - 'Demonstrates the `getFileTypeIconAsUrl()` and `getFileTypeIconAsHTMLString()` utility functions with different DPI settings (1x, 1.5x, 2x) and common file types (docx, pdf, xlsx). These functions are useful when you need direct access to CDN URLs or HTML markup for file type icons.', - }, +UrlAndHtml.parameters = { + docs: { + description: { + story: + 'Demonstrates the `getFileTypeIconAsUrl()` and `getFileTypeIconAsHTMLString()` utility functions with different DPI settings (1x, 1.5x, 2x) and common file types (docx, pdf, xlsx). These functions are useful when you need direct access to CDN URLs or HTML markup for file type icons.', }, }, }; + +UrlAndHtml.storyName = 'URL and HTML Functions'; From c31ef7ea2e47194a1e48a1f47f5697ba3783d47c Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 18:37:14 -0800 Subject: [PATCH 56/69] Add device pixel ratio state and effect to UrlAndHtml component to pass ci/cd test --- .../src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx index 47b449e1e39170..5bdecbd82d1182 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx @@ -70,6 +70,11 @@ const commonFileTypes = ['docx', 'pdf', 'xlsx']; export const UrlAndHtml = (): JSXElement => { const styles = useStyles(); + const [devicePixelRatio, setDevicePixelRatio] = React.useState('N/A'); + + React.useEffect(() => { + setDevicePixelRatio(window.devicePixelRatio); + }, []); return (
@@ -199,7 +204,7 @@ export const UrlAndHtml = (): JSXElement => {
Current Device Information
- Device Pixel Ratio: {typeof window !== 'undefined' ? window.devicePixelRatio : 'N/A'} + Device Pixel Ratio: {devicePixelRatio}
This affects which icon variant is loaded for PNG images. SVG images scale better but may use 1.5x variant for From 6c94223d24488a63ea05b33dfc943516dfe8cfce Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 19:05:25 -0800 Subject: [PATCH 57/69] yarn nx format:write prettier --- .../src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx index 5bdecbd82d1182..9af847edfcc730 100644 --- a/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx +++ b/packages/react-components/react-file-type-icons/stories/src/FileTypeIcon/FileTypeIconUrlAndHtml.stories.tsx @@ -203,9 +203,7 @@ export const UrlAndHtml = (): JSXElement => { {/* Interactive Example */}
Current Device Information
-
- Device Pixel Ratio: {devicePixelRatio} -
+
Device Pixel Ratio: {devicePixelRatio}
This affects which icon variant is loaded for PNG images. SVG images scale better but may use 1.5x variant for better rendering. From 917f8782849cf555d150f10d691e57f9dc207118 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 23:04:53 -0800 Subject: [PATCH 58/69] dead end ci/cd solving ci/main no dice --- .../src/PortalCompat.cy.tsx | 2 +- tools/workspace-plugin/project.json | 8 ++ .../src/executors/build/executor.spec.ts | 32 ++++--- .../src/executors/build/lib/assets.spec.ts | 14 +-- .../src/executors/clean/executor.spec.ts | 8 +- .../executors/generate-api/executor.spec.ts | 10 +- .../generators/eslint-rule/generator.spec.ts | 2 +- .../migrate-converged-pkg/index.spec.ts | 4 +- .../generators/move-packages/index.spec.ts | 94 +++++++++++-------- .../generators/react-component/index.spec.ts | 28 +++--- .../generators/react-library/index.spec.ts | 12 +-- .../split-library-in-two/generator.spec.ts | 10 +- .../src/plugins/testing-utils/temp-fs.ts | 55 ++++++++++- 13 files changed, 177 insertions(+), 102 deletions(-) diff --git a/packages/react-components/react-portal-compat/src/PortalCompat.cy.tsx b/packages/react-components/react-portal-compat/src/PortalCompat.cy.tsx index e564a0a72fd66c..0422af5a5c7479 100644 --- a/packages/react-components/react-portal-compat/src/PortalCompat.cy.tsx +++ b/packages/react-components/react-portal-compat/src/PortalCompat.cy.tsx @@ -3,7 +3,7 @@ import { mount as mountBase } from '@fluentui/scripts-cypress'; import { FluentProvider } from '@fluentui/react-provider'; import { teamsLightTheme } from '@fluentui/react-theme'; import type { JSXElement } from '@fluentui/react-utilities/'; -import { PortalCompatProvider } from '@fluentui/react-portal-compat'; +import { PortalCompatProvider } from './index'; import { usePortalCompat } from '@fluentui/react-portal-compat-context'; const mount = (element: JSXElement) => { diff --git a/tools/workspace-plugin/project.json b/tools/workspace-plugin/project.json index 7fea5bc0475bed..078fa3542d1d4e 100644 --- a/tools/workspace-plugin/project.json +++ b/tools/workspace-plugin/project.json @@ -41,6 +41,14 @@ "options": { "cwd": "{projectRoot}" } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "tools/workspace-plugin/jest.config.ts", + "passWithNoTests": true + } } } } diff --git a/tools/workspace-plugin/src/executors/build/executor.spec.ts b/tools/workspace-plugin/src/executors/build/executor.spec.ts index 73cd97294c5e8f..daa4f06459cf10 100644 --- a/tools/workspace-plugin/src/executors/build/executor.spec.ts +++ b/tools/workspace-plugin/src/executors/build/executor.spec.ts @@ -86,13 +86,15 @@ describe('Build Executor', () => { // mute api extractor - END }); - it('runs build and api-generation and fails on api update', async () => { - const loggerLogSpy = jest.spyOn(logger, 'log').mockImplementation(() => { - return; - }); - const loggerVerboseSpy = jest.spyOn(logger, 'verbose').mockImplementation(() => { - return; - }); + it( + 'runs build and api-generation and fails on api update', + async () => { + const loggerLogSpy = jest.spyOn(logger, 'log').mockImplementation(() => { + return; + }); + const loggerVerboseSpy = jest.spyOn(logger, 'verbose').mockImplementation(() => { + return; + }); const output = await executor(options, context); expect(output.success).toBe(true); @@ -103,9 +105,9 @@ describe('Build Executor', () => { expect(stripIndents`${clearLogs}`).toEqual(stripIndents` Cleaning outputs: - - ${workspaceRoot}/libs/proj/lib-commonjs - - ${workspaceRoot}/libs/proj/lib - - ${workspaceRoot}/libs/proj/dist/assets/spec.md + - ${join(workspaceRoot, 'libs', 'proj', 'lib-commonjs')} + - ${join(workspaceRoot, 'libs', 'proj', 'lib')} + - ${join(workspaceRoot, 'libs', 'proj', 'dist', 'assets', 'spec.md')} `); expect(restOfLogs).toEqual([ @@ -116,22 +118,22 @@ describe('Build Executor', () => { expect(loggerVerboseSpy.mock.calls.flat()).toEqual([ `Applying transforms: 0`, - `babel: transformed ${workspaceRoot}/libs/proj/lib/greeter.styles.js`, + `babel: transformed ${join(workspaceRoot, 'libs', 'proj', 'lib', 'greeter.styles.js')}`, `Applying transforms: 0`, ]); expect(rmMock.mock.calls.flat()).toEqual([ - `${workspaceRoot}/libs/proj/lib-commonjs`, + join(workspaceRoot, 'libs', 'proj', 'lib-commonjs'), { force: true, recursive: true, }, - `${workspaceRoot}/libs/proj/lib`, + join(workspaceRoot, 'libs', 'proj', 'lib'), { force: true, recursive: true, }, - `${workspaceRoot}/libs/proj/dist/assets/spec.md`, + join(workspaceRoot, 'libs', 'proj', 'dist', 'assets', 'spec.md'), { force: true, recursive: true, @@ -387,6 +389,6 @@ describe('Build Executor', () => { expect(existsSync(join(workspaceRoot, 'libs/proj/lib/greeter.styles.raw.js.map'))).toBe(false); expect(existsSync(join(workspaceRoot, 'libs/proj/lib-commonjs/greeter.styles.raw.js'))).toBe(false); expect(existsSync(join(workspaceRoot, 'libs/proj/lib-commonjs/greeter.styles.raw.js.map'))).toBe(false); - }, 60000); + }, 180000); }); }); diff --git a/tools/workspace-plugin/src/executors/build/lib/assets.spec.ts b/tools/workspace-plugin/src/executors/build/lib/assets.spec.ts index f326448aae5fef..06d79d14b17cd9 100644 --- a/tools/workspace-plugin/src/executors/build/lib/assets.spec.ts +++ b/tools/workspace-plugin/src/executors/build/lib/assets.spec.ts @@ -1,4 +1,4 @@ -import { join } from 'node:path'; +import { join, normalize } from 'node:path'; // ==== mock start ==== import { cp } from 'node:fs/promises'; @@ -36,16 +36,16 @@ describe(`assets`, () => { expect(actual).toBe(true); expect(cpMock.mock.calls.flat()).toEqual([ // from - `${rootDir}/libs/proj/world.md`, + join(rootDir, 'libs', 'proj', 'world.md'), // to - `${rootDir}/libs/proj/dist/world.md`, + join(rootDir, 'libs', 'proj', 'dist', 'world.md'), { recursive: true, }, // from - `${rootDir}/libs/proj/hello.txt`, + join(rootDir, 'libs', 'proj', 'hello.txt'), // to - `${rootDir}/libs/proj/dist/copied-assets/hello.txt`, + join(rootDir, 'libs', 'proj', 'dist', 'copied-assets', 'hello.txt'), { recursive: true, }, @@ -72,9 +72,9 @@ describe(`assets`, () => { expect(actual).toBe(true); expect(cpMock.mock.calls.flat()).toEqual([ // from - `${rootDir}/libs/proj/hello.md__tmpl__`, + join(rootDir, 'libs', 'proj', 'hello.md__tmpl__'), // to - `${rootDir}/libs/proj/dist/copied-assets/hello.md`, + join(rootDir, 'libs', 'proj', 'dist', 'copied-assets', 'hello.md'), { recursive: true, }, diff --git a/tools/workspace-plugin/src/executors/clean/executor.spec.ts b/tools/workspace-plugin/src/executors/clean/executor.spec.ts index 38b4bb9d972ee1..72b8107df37cd0 100644 --- a/tools/workspace-plugin/src/executors/clean/executor.spec.ts +++ b/tools/workspace-plugin/src/executors/clean/executor.spec.ts @@ -68,12 +68,12 @@ describe('Clean Executor', () => { expect(output.success).toBe(true); expect(rmMock.mock.calls.flat()).toEqual([ - expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/dist'), + join(projRoot, 'dist'), { force: true, recursive: true, }, - expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/lib'), + join(projRoot, 'lib'), { force: true, recursive: true, @@ -90,12 +90,12 @@ describe('Clean Executor', () => { expect(output.success).toBe(true); expect(rmMock.mock.calls.flat()).toEqual([ - expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/foo-bar'), + join(projRoot, 'foo-bar'), { force: true, recursive: true, }, - expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/mr-wick'), + join(projRoot, 'mr-wick'), { force: true, recursive: true, diff --git a/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts b/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts index 378da85a19c1e8..f32fbfd61bbda4 100644 --- a/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts +++ b/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts @@ -5,7 +5,7 @@ import { type ExtractorConfig, type ExtractorResult, } from '@microsoft/api-extractor'; -import { basename, join } from 'node:path'; +import { basename, join, normalize } from 'node:path'; import { mkdirSync, mkdtempSync, rmSync, writeFileSync, readdirSync } from 'node:fs'; import { type TsConfig } from '../../types'; @@ -143,7 +143,7 @@ describe('GenerateApi Executor', () => { try { await executor(options, context); } catch (err) { - expect(err).toMatchInlineSnapshot(`[Error: ${paths.projRoot}/tsconfig.json doesn't exist]`); + expect((err as Error).message).toContain(`tsconfig.json doesn't exist`); } writeFileSync(join(paths.projRoot, 'tsconfig.json'), '{}', 'utf-8'); @@ -151,9 +151,7 @@ describe('GenerateApi Executor', () => { try { await executor(options, context); } catch (err) { - expect(err).toMatchInlineSnapshot( - `[Error: Cannot find api-extractor.json at "${paths.projRoot}/config/api-extractor.json"]`, - ); + expect((err as Error).message).toContain(`Cannot find api-extractor.json at`); } }); @@ -172,7 +170,7 @@ describe('GenerateApi Executor', () => { const output = await executor(options, context); expect(execSyncMock.mock.calls.flat()).toEqual([ - `tsc -p ${paths.projRoot}/tsconfig.lib.json --pretty --emitDeclarationOnly --baseUrl ${paths.projRoot}`, + `tsc -p ${join(paths.projRoot, 'tsconfig.lib.json')} --pretty --emitDeclarationOnly --baseUrl ${paths.projRoot}`, { stdio: 'inherit' }, ]); diff --git a/tools/workspace-plugin/src/generators/eslint-rule/generator.spec.ts b/tools/workspace-plugin/src/generators/eslint-rule/generator.spec.ts index 0e1ce9e8a9d43b..dde7f64ef41eb3 100644 --- a/tools/workspace-plugin/src/generators/eslint-rule/generator.spec.ts +++ b/tools/workspace-plugin/src/generators/eslint-rule/generator.spec.ts @@ -15,7 +15,7 @@ describe('eslint-rule generator', () => { jest.spyOn(console, 'info').mockImplementation(noop); tree = createTreeWithEmptyWorkspace(); await lintWorkspaceRulesProjectGenerator(tree, {}); - }); + }, 10000); it('should generate new eslint rule', async () => { const config = readProjectConfiguration(tree, 'eslint-rules'); diff --git a/tools/workspace-plugin/src/generators/migrate-converged-pkg/index.spec.ts b/tools/workspace-plugin/src/generators/migrate-converged-pkg/index.spec.ts index 9ddbc96b6aa064..13dce889761dda 100644 --- a/tools/workspace-plugin/src/generators/migrate-converged-pkg/index.spec.ts +++ b/tools/workspace-plugin/src/generators/migrate-converged-pkg/index.spec.ts @@ -1123,7 +1123,7 @@ describe('migrate-converged-pkg generator', () => { let babelConfig = getBabelConfig(projectConfig); expect(babelConfig).toEqual({ - extends: '../../.babelrc-v9.json', + extends: '../../.babelrc-v9.json'.replace(/\//g, '\\'), plugins: ['annotate-pure-calls', '@babel/transform-react-pure-annotations'], }); @@ -1133,7 +1133,7 @@ describe('migrate-converged-pkg generator', () => { babelConfig = getBabelConfig(projectConfig); expect(babelConfig).toEqual({ - extends: '../../.babelrc-v9.json', + extends: '../../.babelrc-v9.json'.replace(/\//g, '\\'), plugins: ['annotate-pure-calls', '@babel/transform-react-pure-annotations'], }); }); diff --git a/tools/workspace-plugin/src/generators/move-packages/index.spec.ts b/tools/workspace-plugin/src/generators/move-packages/index.spec.ts index 2aa5ee14094003..f22b6da21ec468 100644 --- a/tools/workspace-plugin/src/generators/move-packages/index.spec.ts +++ b/tools/workspace-plugin/src/generators/move-packages/index.spec.ts @@ -59,11 +59,15 @@ describe('move-packages generator', () => { }); describe('general', () => { - it('should run successfully', async () => { - await generator(tree, options); - const config = readProjectConfiguration(tree, options.name); - expect(config).toBeDefined(); - }); + it( + 'should run successfully', + async () => { + await generator(tree, options); + const config = readProjectConfiguration(tree, options.name); + expect(config).toBeDefined(); + }, + 10000, + ); describe('schema validation', () => { it('should throw if --name && --allConverged are both specified', async () => { @@ -205,24 +209,28 @@ describe('move-packages generator', () => { setupDummyPackage(tree, { name: '@proj/react-old', version: '8.0.1' }); }); - it(`should move all v9 packages in batch`, async () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const projects = [ - options.name, - '@proj/react-foo', - '@proj/react-bar', - '@proj/react-moo', - '@proj/react-old', - ] as const; - - const destinationFolder = 'testFolder'; - await generator(tree, { allConverged: true, destination: destinationFolder }); - - expect(tree.exists(`packages/${destinationFolder}/react-foo/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-bar/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-moo/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-old/src/index.ts`)).toBeFalsy(); - }); + it( + `should move all v9 packages in batch`, + async () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const projects = [ + options.name, + '@proj/react-foo', + '@proj/react-bar', + '@proj/react-moo', + '@proj/react-old', + ] as const; + + const destinationFolder = 'testFolder'; + await generator(tree, { allConverged: true, destination: destinationFolder }); + + expect(tree.exists(`packages/${destinationFolder}/react-foo/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-bar/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-moo/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-old/src/index.ts`)).toBeFalsy(); + }, + 10000, + ); }); describe('--allV8', () => { @@ -233,24 +241,28 @@ describe('move-packages generator', () => { setupDummyPackage(tree, { name: '@proj/react-old', version: '9.0.1' }); }); - it(`should move all v8 packages in batch`, async () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const projects = [ - options.name, - '@proj/react-foo', - '@proj/react-bar', - '@proj/react-moo', - '@proj/react-old', - ] as const; - - const destinationFolder = 'testFolder'; - await generator(tree, { allV8: true, destination: destinationFolder }); - - expect(tree.exists(`packages/${destinationFolder}/react-foo/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-bar/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-moo/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-old/src/index.ts`)).toBeFalsy(); - }); + it( + `should move all v8 packages in batch`, + async () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const projects = [ + options.name, + '@proj/react-foo', + '@proj/react-bar', + '@proj/react-moo', + '@proj/react-old', + ] as const; + + const destinationFolder = 'testFolder'; + await generator(tree, { allV8: true, destination: destinationFolder }); + + expect(tree.exists(`packages/${destinationFolder}/react-foo/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-bar/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-moo/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-old/src/index.ts`)).toBeFalsy(); + }, + 10000, + ); }); }); diff --git a/tools/workspace-plugin/src/generators/react-component/index.spec.ts b/tools/workspace-plugin/src/generators/react-component/index.spec.ts index 80f342159309c0..eff4c2f3eb7328 100644 --- a/tools/workspace-plugin/src/generators/react-component/index.spec.ts +++ b/tools/workspace-plugin/src/generators/react-component/index.spec.ts @@ -20,16 +20,20 @@ describe('react-component generator', () => { } }); - it(`should throw error if component already exists`, async () => { - createLibrary(tree, 'react-one'); - await generator(tree, { project: 'react-one', name: 'MyOne' }); - - try { + it( + `should throw error if component already exists`, + async () => { + createLibrary(tree, 'react-one'); await generator(tree, { project: 'react-one', name: 'MyOne' }); - } catch (err) { - expect(err).toMatchInlineSnapshot(`[Error: The component "MyOne" already exists]`); - } - }); + + try { + await generator(tree, { project: 'react-one', name: 'MyOne' }); + } catch (err) { + expect(err).toMatchInlineSnapshot(`[Error: The component "MyOne" already exists]`); + } + }, + 10000, + ); }); describe(`component`, () => { @@ -70,7 +74,8 @@ describe('react-component generator', () => { " `); - expect(tree.children(componentRootPath)).toMatchInlineSnapshot(` + const children = tree.children(componentRootPath).sort(); + expect(children).toMatchInlineSnapshot(` Array [ "MyOne.test.tsx", "MyOne.tsx", @@ -267,7 +272,8 @@ describe('react-component generator', () => { : 'packages/react-components/react-one/stories/src/MyOne'; await generator(tree, { project: 'react-one', name: 'MyOne' }); - expect(tree.children(componentStoryRootPath)).toMatchInlineSnapshot(` + const storyChildren = tree.children(componentStoryRootPath).sort(); + expect(storyChildren).toMatchInlineSnapshot(` Array [ "MyOneBestPractices.md", "MyOneDefault.stories.tsx", diff --git a/tools/workspace-plugin/src/generators/react-library/index.spec.ts b/tools/workspace-plugin/src/generators/react-library/index.spec.ts index 0bc3f897812ec4..dea5a6563c8629 100644 --- a/tools/workspace-plugin/src/generators/react-library/index.spec.ts +++ b/tools/workspace-plugin/src/generators/react-library/index.spec.ts @@ -55,14 +55,14 @@ describe('react-library generator', () => { "project.json", ".babelrc.json", ".swcrc", - "LICENSE", - "README.md", "config", "docs", "eslint.config.js", "etc", "jest.config.js", + "LICENSE", "package.json", + "README.md", "src", "tsconfig.json", "tsconfig.lib.json", @@ -249,7 +249,7 @@ describe('react-library generator', () => { packages/react-components/react-one-preview/stories @org/chosen-one `), ); - }); + }, 10000); it(`should create compat package`, async () => { setup(tree); @@ -265,14 +265,14 @@ describe('react-library generator', () => { "project.json", ".babelrc.json", ".swcrc", - "LICENSE", - "README.md", "config", "docs", "eslint.config.js", "etc", "jest.config.js", + "LICENSE", "package.json", + "README.md", "src", "tsconfig.json", "tsconfig.lib.json", @@ -309,7 +309,7 @@ describe('react-library generator', () => { tags: ['vNext', 'platform:web', 'compat', 'type:stories'], }), ); - }); + }, 10000); }); function setup(tree: Tree) { diff --git a/tools/workspace-plugin/src/generators/split-library-in-two/generator.spec.ts b/tools/workspace-plugin/src/generators/split-library-in-two/generator.spec.ts index 3b1ecf8b4eb22a..fdf1305eeb5485 100644 --- a/tools/workspace-plugin/src/generators/split-library-in-two/generator.spec.ts +++ b/tools/workspace-plugin/src/generators/split-library-in-two/generator.spec.ts @@ -34,10 +34,12 @@ describe('split-library-in-two generator', () => { jest.spyOn(output, 'error').mockImplementation(noop); }); - it('should split v9 project into 2', async () => { - const oldConfig = readProjectConfiguration(tree, options.project); + it( + 'should split v9 project into 2', + async () => { + const oldConfig = readProjectConfiguration(tree, options.project); - await splitLibraryInTwoGenerator(tree, options); + await splitLibraryInTwoGenerator(tree, options); const newConfig = readProjectConfiguration(tree, options.project); const storiesConfig = readProjectConfiguration(tree, `${options.project}-stories`); @@ -324,7 +326,7 @@ describe('split-library-in-two generator', () => { export const tags = ['autodocs']; " `); - }); + }, 10000); }); function setup(tree: Tree) { diff --git a/tools/workspace-plugin/src/plugins/testing-utils/temp-fs.ts b/tools/workspace-plugin/src/plugins/testing-utils/temp-fs.ts index 46b97b314a24e8..3a280fa7fa43ea 100644 --- a/tools/workspace-plugin/src/plugins/testing-utils/temp-fs.ts +++ b/tools/workspace-plugin/src/plugins/testing-utils/temp-fs.ts @@ -90,12 +90,59 @@ export class TempFs { } cleanup() { - rmSync(this.tempDir, { recursive: true, force: true }); - setWorkspaceRoot(this.previousWorkspaceRoot); + // Retry cleanup on Windows to handle EBUSY errors + let attempts = 0; + const maxAttempts = 3; + while (attempts < maxAttempts) { + try { + rmSync(this.tempDir, { recursive: true, force: true }); + setWorkspaceRoot(this.previousWorkspaceRoot); + return; + } catch (error: any) { + attempts++; + if (attempts >= maxAttempts || !error?.code || error.code !== 'EBUSY') { + // If we've exhausted retries or it's a different error, just continue + // Don't throw to avoid breaking tests + console.warn(`Failed to cleanup temp directory after ${attempts} attempts:`, error.message); + setWorkspaceRoot(this.previousWorkspaceRoot); + return; + } + // Wait before retry with exponential backoff + const delay = 100 * Math.pow(2, attempts - 1); + const start = Date.now(); + while (Date.now() - start < delay) { + // Busy wait + } + } + } } reset() { - rmSync(this.tempDir, { recursive: true, force: true }); - mkdirSync(this.tempDir, { recursive: true }); + // Retry reset on Windows to handle EBUSY errors + let attempts = 0; + const maxAttempts = 3; + while (attempts < maxAttempts) { + try { + rmSync(this.tempDir, { recursive: true, force: true }); + mkdirSync(this.tempDir, { recursive: true }); + return; + } catch (error: any) { + attempts++; + if (attempts >= maxAttempts || !error?.code || error.code !== 'EBUSY') { + console.warn(`Failed to reset temp directory after ${attempts} attempts:`, error.message); + // Try to at least create the directory if it doesn't exist + if (!existsSync(this.tempDir)) { + mkdirSync(this.tempDir, { recursive: true }); + } + return; + } + // Wait before retry with exponential backoff + const delay = 100 * Math.pow(2, attempts - 1); + const start = Date.now(); + while (Date.now() - start < delay) { + // Busy wait + } + } + } } } From f1984d181a405ad2ee698a17afa5e54365c830dc Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 23:20:34 -0800 Subject: [PATCH 59/69] first late undo --- ...-3335c1ee-ca00-4b7f-b234-b8326800843d.json | 7 -- .../chart-web-components/CHANGELOG.json | 15 +++ .../charts/chart-web-components/CHANGELOG.md | 11 ++- .../charts/chart-web-components/package.json | 4 +- .../src/PortalCompat.cy.tsx | 2 +- packages/web-components/CHANGELOG.json | 15 +++ packages/web-components/CHANGELOG.md | 11 ++- packages/web-components/package.json | 2 +- tools/workspace-plugin/project.json | 8 -- .../src/executors/build/executor.spec.ts | 32 +++---- .../src/executors/build/lib/assets.spec.ts | 14 +-- .../src/executors/clean/executor.spec.ts | 8 +- .../executors/generate-api/executor.spec.ts | 10 +- .../generators/eslint-rule/generator.spec.ts | 2 +- .../migrate-converged-pkg/index.spec.ts | 4 +- .../generators/move-packages/index.spec.ts | 94 ++++++++----------- .../generators/react-component/index.spec.ts | 28 +++--- .../generators/react-library/index.spec.ts | 12 +-- .../split-library-in-two/generator.spec.ts | 10 +- .../src/plugins/testing-utils/temp-fs.ts | 55 +---------- 20 files changed, 155 insertions(+), 189 deletions(-) delete mode 100644 change/@fluentui-web-components-3335c1ee-ca00-4b7f-b234-b8326800843d.json diff --git a/change/@fluentui-web-components-3335c1ee-ca00-4b7f-b234-b8326800843d.json b/change/@fluentui-web-components-3335c1ee-ca00-4b7f-b234-b8326800843d.json deleted file mode 100644 index a865801d178161..00000000000000 --- a/change/@fluentui-web-components-3335c1ee-ca00-4b7f-b234-b8326800843d.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "move web components package from beta to release candidate", - "packageName": "@fluentui/web-components", - "email": "13071055+chrisdholt@users.noreply.github.com", - "dependentChangeType": "patch" -} diff --git a/packages/charts/chart-web-components/CHANGELOG.json b/packages/charts/chart-web-components/CHANGELOG.json index 45f563fb3e75e7..49a43743e6bf02 100644 --- a/packages/charts/chart-web-components/CHANGELOG.json +++ b/packages/charts/chart-web-components/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/chart-web-components", "entries": [ + { + "date": "Wed, 26 Nov 2025 04:06:59 GMT", + "tag": "@fluentui/chart-web-components_v0.0.59", + "version": "0.0.59", + "comments": { + "patch": [ + { + "author": "beachball", + "package": "@fluentui/chart-web-components", + "comment": "Bump @fluentui/web-components to v3.0.0-rc.1", + "commit": "0c388e45a89a459efa4d8dc023fcebeadccc4f40" + } + ] + } + }, { "date": "Wed, 12 Nov 2025 04:07:28 GMT", "tag": "@fluentui/chart-web-components_v0.0.58", diff --git a/packages/charts/chart-web-components/CHANGELOG.md b/packages/charts/chart-web-components/CHANGELOG.md index 2172899e5ace58..da1e352ff09d19 100644 --- a/packages/charts/chart-web-components/CHANGELOG.md +++ b/packages/charts/chart-web-components/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/chart-web-components -This log was last generated on Wed, 12 Nov 2025 04:07:28 GMT and should not be manually modified. +This log was last generated on Wed, 26 Nov 2025 04:06:59 GMT and should not be manually modified. +## [0.0.59](https://github.com/microsoft/fluentui/tree/@fluentui/chart-web-components_v0.0.59) + +Wed, 26 Nov 2025 04:06:59 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/chart-web-components_v0.0.58..@fluentui/chart-web-components_v0.0.59) + +### Patches + +- Bump @fluentui/web-components to v3.0.0-rc.1 ([PR #35499](https://github.com/microsoft/fluentui/pull/35499) by beachball) + ## [0.0.58](https://github.com/microsoft/fluentui/tree/@fluentui/chart-web-components_v0.0.58) Wed, 12 Nov 2025 04:07:28 GMT diff --git a/packages/charts/chart-web-components/package.json b/packages/charts/chart-web-components/package.json index 5029e66da8feb5..bc15d1fd30df27 100644 --- a/packages/charts/chart-web-components/package.json +++ b/packages/charts/chart-web-components/package.json @@ -1,7 +1,7 @@ { "name": "@fluentui/chart-web-components", "description": "A library of Fluent Chart Web Components", - "version": "0.0.58", + "version": "0.0.59", "author": { "name": "Microsoft" }, @@ -71,7 +71,7 @@ "dependencies": { "@microsoft/fast-web-utilities": "^6.0.0", "@fluentui/tokens": "^1.0.0-alpha.22", - "@fluentui/web-components": "^3.0.0-beta.133", + "@fluentui/web-components": "^3.0.0-rc.1", "@types/d3-selection": "^3.0.0", "@types/d3-shape": "^3.0.0", "d3-selection": "^3.0.0", diff --git a/packages/react-components/react-portal-compat/src/PortalCompat.cy.tsx b/packages/react-components/react-portal-compat/src/PortalCompat.cy.tsx index 0422af5a5c7479..e564a0a72fd66c 100644 --- a/packages/react-components/react-portal-compat/src/PortalCompat.cy.tsx +++ b/packages/react-components/react-portal-compat/src/PortalCompat.cy.tsx @@ -3,7 +3,7 @@ import { mount as mountBase } from '@fluentui/scripts-cypress'; import { FluentProvider } from '@fluentui/react-provider'; import { teamsLightTheme } from '@fluentui/react-theme'; import type { JSXElement } from '@fluentui/react-utilities/'; -import { PortalCompatProvider } from './index'; +import { PortalCompatProvider } from '@fluentui/react-portal-compat'; import { usePortalCompat } from '@fluentui/react-portal-compat-context'; const mount = (element: JSXElement) => { diff --git a/packages/web-components/CHANGELOG.json b/packages/web-components/CHANGELOG.json index 92d289a10d4ce7..9f173d23cee0bf 100644 --- a/packages/web-components/CHANGELOG.json +++ b/packages/web-components/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@fluentui/web-components", "entries": [ + { + "date": "Wed, 26 Nov 2025 04:06:59 GMT", + "tag": "@fluentui/web-components_v3.0.0-rc.1", + "version": "3.0.0-rc.1", + "comments": { + "prerelease": [ + { + "author": "13071055+chrisdholt@users.noreply.github.com", + "package": "@fluentui/web-components", + "commit": "0c388e45a89a459efa4d8dc023fcebeadccc4f40", + "comment": "move web components package from beta to release candidate" + } + ] + } + }, { "date": "Wed, 12 Nov 2025 04:07:28 GMT", "tag": "@fluentui/web-components_v3.0.0-beta.133", diff --git a/packages/web-components/CHANGELOG.md b/packages/web-components/CHANGELOG.md index ac91de83d0cf64..da3b82dba6ce0d 100644 --- a/packages/web-components/CHANGELOG.md +++ b/packages/web-components/CHANGELOG.md @@ -1,9 +1,18 @@ # Change Log - @fluentui/web-components -This log was last generated on Wed, 12 Nov 2025 04:07:28 GMT and should not be manually modified. +This log was last generated on Wed, 26 Nov 2025 04:06:59 GMT and should not be manually modified. +## [3.0.0-rc.1](https://github.com/microsoft/fluentui/tree/@fluentui/web-components_v3.0.0-rc.1) + +Wed, 26 Nov 2025 04:06:59 GMT +[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/web-components_v3.0.0-beta.133..@fluentui/web-components_v3.0.0-rc.1) + +### Changes + +- move web components package from beta to release candidate ([PR #35499](https://github.com/microsoft/fluentui/pull/35499) by 13071055+chrisdholt@users.noreply.github.com) + ## [3.0.0-beta.133](https://github.com/microsoft/fluentui/tree/@fluentui/web-components_v3.0.0-beta.133) Wed, 12 Nov 2025 04:07:28 GMT diff --git a/packages/web-components/package.json b/packages/web-components/package.json index 6e343fb2719335..7fda873e49f2c2 100644 --- a/packages/web-components/package.json +++ b/packages/web-components/package.json @@ -1,7 +1,7 @@ { "name": "@fluentui/web-components", "description": "A library of Fluent Web Components", - "version": "3.0.0-rc.0", + "version": "3.0.0-rc.1", "author": { "name": "Microsoft", "url": "https://discord.gg/FcSNfg4" diff --git a/tools/workspace-plugin/project.json b/tools/workspace-plugin/project.json index 078fa3542d1d4e..7fea5bc0475bed 100644 --- a/tools/workspace-plugin/project.json +++ b/tools/workspace-plugin/project.json @@ -41,14 +41,6 @@ "options": { "cwd": "{projectRoot}" } - }, - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "tools/workspace-plugin/jest.config.ts", - "passWithNoTests": true - } } } } diff --git a/tools/workspace-plugin/src/executors/build/executor.spec.ts b/tools/workspace-plugin/src/executors/build/executor.spec.ts index daa4f06459cf10..73cd97294c5e8f 100644 --- a/tools/workspace-plugin/src/executors/build/executor.spec.ts +++ b/tools/workspace-plugin/src/executors/build/executor.spec.ts @@ -86,15 +86,13 @@ describe('Build Executor', () => { // mute api extractor - END }); - it( - 'runs build and api-generation and fails on api update', - async () => { - const loggerLogSpy = jest.spyOn(logger, 'log').mockImplementation(() => { - return; - }); - const loggerVerboseSpy = jest.spyOn(logger, 'verbose').mockImplementation(() => { - return; - }); + it('runs build and api-generation and fails on api update', async () => { + const loggerLogSpy = jest.spyOn(logger, 'log').mockImplementation(() => { + return; + }); + const loggerVerboseSpy = jest.spyOn(logger, 'verbose').mockImplementation(() => { + return; + }); const output = await executor(options, context); expect(output.success).toBe(true); @@ -105,9 +103,9 @@ describe('Build Executor', () => { expect(stripIndents`${clearLogs}`).toEqual(stripIndents` Cleaning outputs: - - ${join(workspaceRoot, 'libs', 'proj', 'lib-commonjs')} - - ${join(workspaceRoot, 'libs', 'proj', 'lib')} - - ${join(workspaceRoot, 'libs', 'proj', 'dist', 'assets', 'spec.md')} + - ${workspaceRoot}/libs/proj/lib-commonjs + - ${workspaceRoot}/libs/proj/lib + - ${workspaceRoot}/libs/proj/dist/assets/spec.md `); expect(restOfLogs).toEqual([ @@ -118,22 +116,22 @@ describe('Build Executor', () => { expect(loggerVerboseSpy.mock.calls.flat()).toEqual([ `Applying transforms: 0`, - `babel: transformed ${join(workspaceRoot, 'libs', 'proj', 'lib', 'greeter.styles.js')}`, + `babel: transformed ${workspaceRoot}/libs/proj/lib/greeter.styles.js`, `Applying transforms: 0`, ]); expect(rmMock.mock.calls.flat()).toEqual([ - join(workspaceRoot, 'libs', 'proj', 'lib-commonjs'), + `${workspaceRoot}/libs/proj/lib-commonjs`, { force: true, recursive: true, }, - join(workspaceRoot, 'libs', 'proj', 'lib'), + `${workspaceRoot}/libs/proj/lib`, { force: true, recursive: true, }, - join(workspaceRoot, 'libs', 'proj', 'dist', 'assets', 'spec.md'), + `${workspaceRoot}/libs/proj/dist/assets/spec.md`, { force: true, recursive: true, @@ -389,6 +387,6 @@ describe('Build Executor', () => { expect(existsSync(join(workspaceRoot, 'libs/proj/lib/greeter.styles.raw.js.map'))).toBe(false); expect(existsSync(join(workspaceRoot, 'libs/proj/lib-commonjs/greeter.styles.raw.js'))).toBe(false); expect(existsSync(join(workspaceRoot, 'libs/proj/lib-commonjs/greeter.styles.raw.js.map'))).toBe(false); - }, 180000); + }, 60000); }); }); diff --git a/tools/workspace-plugin/src/executors/build/lib/assets.spec.ts b/tools/workspace-plugin/src/executors/build/lib/assets.spec.ts index 06d79d14b17cd9..f326448aae5fef 100644 --- a/tools/workspace-plugin/src/executors/build/lib/assets.spec.ts +++ b/tools/workspace-plugin/src/executors/build/lib/assets.spec.ts @@ -1,4 +1,4 @@ -import { join, normalize } from 'node:path'; +import { join } from 'node:path'; // ==== mock start ==== import { cp } from 'node:fs/promises'; @@ -36,16 +36,16 @@ describe(`assets`, () => { expect(actual).toBe(true); expect(cpMock.mock.calls.flat()).toEqual([ // from - join(rootDir, 'libs', 'proj', 'world.md'), + `${rootDir}/libs/proj/world.md`, // to - join(rootDir, 'libs', 'proj', 'dist', 'world.md'), + `${rootDir}/libs/proj/dist/world.md`, { recursive: true, }, // from - join(rootDir, 'libs', 'proj', 'hello.txt'), + `${rootDir}/libs/proj/hello.txt`, // to - join(rootDir, 'libs', 'proj', 'dist', 'copied-assets', 'hello.txt'), + `${rootDir}/libs/proj/dist/copied-assets/hello.txt`, { recursive: true, }, @@ -72,9 +72,9 @@ describe(`assets`, () => { expect(actual).toBe(true); expect(cpMock.mock.calls.flat()).toEqual([ // from - join(rootDir, 'libs', 'proj', 'hello.md__tmpl__'), + `${rootDir}/libs/proj/hello.md__tmpl__`, // to - join(rootDir, 'libs', 'proj', 'dist', 'copied-assets', 'hello.md'), + `${rootDir}/libs/proj/dist/copied-assets/hello.md`, { recursive: true, }, diff --git a/tools/workspace-plugin/src/executors/clean/executor.spec.ts b/tools/workspace-plugin/src/executors/clean/executor.spec.ts index 72b8107df37cd0..38b4bb9d972ee1 100644 --- a/tools/workspace-plugin/src/executors/clean/executor.spec.ts +++ b/tools/workspace-plugin/src/executors/clean/executor.spec.ts @@ -68,12 +68,12 @@ describe('Clean Executor', () => { expect(output.success).toBe(true); expect(rmMock.mock.calls.flat()).toEqual([ - join(projRoot, 'dist'), + expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/dist'), { force: true, recursive: true, }, - join(projRoot, 'lib'), + expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/lib'), { force: true, recursive: true, @@ -90,12 +90,12 @@ describe('Clean Executor', () => { expect(output.success).toBe(true); expect(rmMock.mock.calls.flat()).toEqual([ - join(projRoot, 'foo-bar'), + expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/foo-bar'), { force: true, recursive: true, }, - join(projRoot, 'mr-wick'), + expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/mr-wick'), { force: true, recursive: true, diff --git a/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts b/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts index f32fbfd61bbda4..378da85a19c1e8 100644 --- a/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts +++ b/tools/workspace-plugin/src/executors/generate-api/executor.spec.ts @@ -5,7 +5,7 @@ import { type ExtractorConfig, type ExtractorResult, } from '@microsoft/api-extractor'; -import { basename, join, normalize } from 'node:path'; +import { basename, join } from 'node:path'; import { mkdirSync, mkdtempSync, rmSync, writeFileSync, readdirSync } from 'node:fs'; import { type TsConfig } from '../../types'; @@ -143,7 +143,7 @@ describe('GenerateApi Executor', () => { try { await executor(options, context); } catch (err) { - expect((err as Error).message).toContain(`tsconfig.json doesn't exist`); + expect(err).toMatchInlineSnapshot(`[Error: ${paths.projRoot}/tsconfig.json doesn't exist]`); } writeFileSync(join(paths.projRoot, 'tsconfig.json'), '{}', 'utf-8'); @@ -151,7 +151,9 @@ describe('GenerateApi Executor', () => { try { await executor(options, context); } catch (err) { - expect((err as Error).message).toContain(`Cannot find api-extractor.json at`); + expect(err).toMatchInlineSnapshot( + `[Error: Cannot find api-extractor.json at "${paths.projRoot}/config/api-extractor.json"]`, + ); } }); @@ -170,7 +172,7 @@ describe('GenerateApi Executor', () => { const output = await executor(options, context); expect(execSyncMock.mock.calls.flat()).toEqual([ - `tsc -p ${join(paths.projRoot, 'tsconfig.lib.json')} --pretty --emitDeclarationOnly --baseUrl ${paths.projRoot}`, + `tsc -p ${paths.projRoot}/tsconfig.lib.json --pretty --emitDeclarationOnly --baseUrl ${paths.projRoot}`, { stdio: 'inherit' }, ]); diff --git a/tools/workspace-plugin/src/generators/eslint-rule/generator.spec.ts b/tools/workspace-plugin/src/generators/eslint-rule/generator.spec.ts index dde7f64ef41eb3..0e1ce9e8a9d43b 100644 --- a/tools/workspace-plugin/src/generators/eslint-rule/generator.spec.ts +++ b/tools/workspace-plugin/src/generators/eslint-rule/generator.spec.ts @@ -15,7 +15,7 @@ describe('eslint-rule generator', () => { jest.spyOn(console, 'info').mockImplementation(noop); tree = createTreeWithEmptyWorkspace(); await lintWorkspaceRulesProjectGenerator(tree, {}); - }, 10000); + }); it('should generate new eslint rule', async () => { const config = readProjectConfiguration(tree, 'eslint-rules'); diff --git a/tools/workspace-plugin/src/generators/migrate-converged-pkg/index.spec.ts b/tools/workspace-plugin/src/generators/migrate-converged-pkg/index.spec.ts index 13dce889761dda..9ddbc96b6aa064 100644 --- a/tools/workspace-plugin/src/generators/migrate-converged-pkg/index.spec.ts +++ b/tools/workspace-plugin/src/generators/migrate-converged-pkg/index.spec.ts @@ -1123,7 +1123,7 @@ describe('migrate-converged-pkg generator', () => { let babelConfig = getBabelConfig(projectConfig); expect(babelConfig).toEqual({ - extends: '../../.babelrc-v9.json'.replace(/\//g, '\\'), + extends: '../../.babelrc-v9.json', plugins: ['annotate-pure-calls', '@babel/transform-react-pure-annotations'], }); @@ -1133,7 +1133,7 @@ describe('migrate-converged-pkg generator', () => { babelConfig = getBabelConfig(projectConfig); expect(babelConfig).toEqual({ - extends: '../../.babelrc-v9.json'.replace(/\//g, '\\'), + extends: '../../.babelrc-v9.json', plugins: ['annotate-pure-calls', '@babel/transform-react-pure-annotations'], }); }); diff --git a/tools/workspace-plugin/src/generators/move-packages/index.spec.ts b/tools/workspace-plugin/src/generators/move-packages/index.spec.ts index f22b6da21ec468..2aa5ee14094003 100644 --- a/tools/workspace-plugin/src/generators/move-packages/index.spec.ts +++ b/tools/workspace-plugin/src/generators/move-packages/index.spec.ts @@ -59,15 +59,11 @@ describe('move-packages generator', () => { }); describe('general', () => { - it( - 'should run successfully', - async () => { - await generator(tree, options); - const config = readProjectConfiguration(tree, options.name); - expect(config).toBeDefined(); - }, - 10000, - ); + it('should run successfully', async () => { + await generator(tree, options); + const config = readProjectConfiguration(tree, options.name); + expect(config).toBeDefined(); + }); describe('schema validation', () => { it('should throw if --name && --allConverged are both specified', async () => { @@ -209,28 +205,24 @@ describe('move-packages generator', () => { setupDummyPackage(tree, { name: '@proj/react-old', version: '8.0.1' }); }); - it( - `should move all v9 packages in batch`, - async () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const projects = [ - options.name, - '@proj/react-foo', - '@proj/react-bar', - '@proj/react-moo', - '@proj/react-old', - ] as const; - - const destinationFolder = 'testFolder'; - await generator(tree, { allConverged: true, destination: destinationFolder }); - - expect(tree.exists(`packages/${destinationFolder}/react-foo/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-bar/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-moo/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-old/src/index.ts`)).toBeFalsy(); - }, - 10000, - ); + it(`should move all v9 packages in batch`, async () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const projects = [ + options.name, + '@proj/react-foo', + '@proj/react-bar', + '@proj/react-moo', + '@proj/react-old', + ] as const; + + const destinationFolder = 'testFolder'; + await generator(tree, { allConverged: true, destination: destinationFolder }); + + expect(tree.exists(`packages/${destinationFolder}/react-foo/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-bar/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-moo/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-old/src/index.ts`)).toBeFalsy(); + }); }); describe('--allV8', () => { @@ -241,28 +233,24 @@ describe('move-packages generator', () => { setupDummyPackage(tree, { name: '@proj/react-old', version: '9.0.1' }); }); - it( - `should move all v8 packages in batch`, - async () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const projects = [ - options.name, - '@proj/react-foo', - '@proj/react-bar', - '@proj/react-moo', - '@proj/react-old', - ] as const; - - const destinationFolder = 'testFolder'; - await generator(tree, { allV8: true, destination: destinationFolder }); - - expect(tree.exists(`packages/${destinationFolder}/react-foo/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-bar/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-moo/src/index.ts`)).toBeTruthy(); - expect(tree.exists(`packages/${destinationFolder}/react-old/src/index.ts`)).toBeFalsy(); - }, - 10000, - ); + it(`should move all v8 packages in batch`, async () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const projects = [ + options.name, + '@proj/react-foo', + '@proj/react-bar', + '@proj/react-moo', + '@proj/react-old', + ] as const; + + const destinationFolder = 'testFolder'; + await generator(tree, { allV8: true, destination: destinationFolder }); + + expect(tree.exists(`packages/${destinationFolder}/react-foo/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-bar/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-moo/src/index.ts`)).toBeTruthy(); + expect(tree.exists(`packages/${destinationFolder}/react-old/src/index.ts`)).toBeFalsy(); + }); }); }); diff --git a/tools/workspace-plugin/src/generators/react-component/index.spec.ts b/tools/workspace-plugin/src/generators/react-component/index.spec.ts index eff4c2f3eb7328..80f342159309c0 100644 --- a/tools/workspace-plugin/src/generators/react-component/index.spec.ts +++ b/tools/workspace-plugin/src/generators/react-component/index.spec.ts @@ -20,20 +20,16 @@ describe('react-component generator', () => { } }); - it( - `should throw error if component already exists`, - async () => { - createLibrary(tree, 'react-one'); - await generator(tree, { project: 'react-one', name: 'MyOne' }); + it(`should throw error if component already exists`, async () => { + createLibrary(tree, 'react-one'); + await generator(tree, { project: 'react-one', name: 'MyOne' }); - try { - await generator(tree, { project: 'react-one', name: 'MyOne' }); - } catch (err) { - expect(err).toMatchInlineSnapshot(`[Error: The component "MyOne" already exists]`); - } - }, - 10000, - ); + try { + await generator(tree, { project: 'react-one', name: 'MyOne' }); + } catch (err) { + expect(err).toMatchInlineSnapshot(`[Error: The component "MyOne" already exists]`); + } + }); }); describe(`component`, () => { @@ -74,8 +70,7 @@ describe('react-component generator', () => { " `); - const children = tree.children(componentRootPath).sort(); - expect(children).toMatchInlineSnapshot(` + expect(tree.children(componentRootPath)).toMatchInlineSnapshot(` Array [ "MyOne.test.tsx", "MyOne.tsx", @@ -272,8 +267,7 @@ describe('react-component generator', () => { : 'packages/react-components/react-one/stories/src/MyOne'; await generator(tree, { project: 'react-one', name: 'MyOne' }); - const storyChildren = tree.children(componentStoryRootPath).sort(); - expect(storyChildren).toMatchInlineSnapshot(` + expect(tree.children(componentStoryRootPath)).toMatchInlineSnapshot(` Array [ "MyOneBestPractices.md", "MyOneDefault.stories.tsx", diff --git a/tools/workspace-plugin/src/generators/react-library/index.spec.ts b/tools/workspace-plugin/src/generators/react-library/index.spec.ts index dea5a6563c8629..0bc3f897812ec4 100644 --- a/tools/workspace-plugin/src/generators/react-library/index.spec.ts +++ b/tools/workspace-plugin/src/generators/react-library/index.spec.ts @@ -55,14 +55,14 @@ describe('react-library generator', () => { "project.json", ".babelrc.json", ".swcrc", + "LICENSE", + "README.md", "config", "docs", "eslint.config.js", "etc", "jest.config.js", - "LICENSE", "package.json", - "README.md", "src", "tsconfig.json", "tsconfig.lib.json", @@ -249,7 +249,7 @@ describe('react-library generator', () => { packages/react-components/react-one-preview/stories @org/chosen-one `), ); - }, 10000); + }); it(`should create compat package`, async () => { setup(tree); @@ -265,14 +265,14 @@ describe('react-library generator', () => { "project.json", ".babelrc.json", ".swcrc", + "LICENSE", + "README.md", "config", "docs", "eslint.config.js", "etc", "jest.config.js", - "LICENSE", "package.json", - "README.md", "src", "tsconfig.json", "tsconfig.lib.json", @@ -309,7 +309,7 @@ describe('react-library generator', () => { tags: ['vNext', 'platform:web', 'compat', 'type:stories'], }), ); - }, 10000); + }); }); function setup(tree: Tree) { diff --git a/tools/workspace-plugin/src/generators/split-library-in-two/generator.spec.ts b/tools/workspace-plugin/src/generators/split-library-in-two/generator.spec.ts index fdf1305eeb5485..3b1ecf8b4eb22a 100644 --- a/tools/workspace-plugin/src/generators/split-library-in-two/generator.spec.ts +++ b/tools/workspace-plugin/src/generators/split-library-in-two/generator.spec.ts @@ -34,12 +34,10 @@ describe('split-library-in-two generator', () => { jest.spyOn(output, 'error').mockImplementation(noop); }); - it( - 'should split v9 project into 2', - async () => { - const oldConfig = readProjectConfiguration(tree, options.project); + it('should split v9 project into 2', async () => { + const oldConfig = readProjectConfiguration(tree, options.project); - await splitLibraryInTwoGenerator(tree, options); + await splitLibraryInTwoGenerator(tree, options); const newConfig = readProjectConfiguration(tree, options.project); const storiesConfig = readProjectConfiguration(tree, `${options.project}-stories`); @@ -326,7 +324,7 @@ describe('split-library-in-two generator', () => { export const tags = ['autodocs']; " `); - }, 10000); + }); }); function setup(tree: Tree) { diff --git a/tools/workspace-plugin/src/plugins/testing-utils/temp-fs.ts b/tools/workspace-plugin/src/plugins/testing-utils/temp-fs.ts index 3a280fa7fa43ea..46b97b314a24e8 100644 --- a/tools/workspace-plugin/src/plugins/testing-utils/temp-fs.ts +++ b/tools/workspace-plugin/src/plugins/testing-utils/temp-fs.ts @@ -90,59 +90,12 @@ export class TempFs { } cleanup() { - // Retry cleanup on Windows to handle EBUSY errors - let attempts = 0; - const maxAttempts = 3; - while (attempts < maxAttempts) { - try { - rmSync(this.tempDir, { recursive: true, force: true }); - setWorkspaceRoot(this.previousWorkspaceRoot); - return; - } catch (error: any) { - attempts++; - if (attempts >= maxAttempts || !error?.code || error.code !== 'EBUSY') { - // If we've exhausted retries or it's a different error, just continue - // Don't throw to avoid breaking tests - console.warn(`Failed to cleanup temp directory after ${attempts} attempts:`, error.message); - setWorkspaceRoot(this.previousWorkspaceRoot); - return; - } - // Wait before retry with exponential backoff - const delay = 100 * Math.pow(2, attempts - 1); - const start = Date.now(); - while (Date.now() - start < delay) { - // Busy wait - } - } - } + rmSync(this.tempDir, { recursive: true, force: true }); + setWorkspaceRoot(this.previousWorkspaceRoot); } reset() { - // Retry reset on Windows to handle EBUSY errors - let attempts = 0; - const maxAttempts = 3; - while (attempts < maxAttempts) { - try { - rmSync(this.tempDir, { recursive: true, force: true }); - mkdirSync(this.tempDir, { recursive: true }); - return; - } catch (error: any) { - attempts++; - if (attempts >= maxAttempts || !error?.code || error.code !== 'EBUSY') { - console.warn(`Failed to reset temp directory after ${attempts} attempts:`, error.message); - // Try to at least create the directory if it doesn't exist - if (!existsSync(this.tempDir)) { - mkdirSync(this.tempDir, { recursive: true }); - } - return; - } - // Wait before retry with exponential backoff - const delay = 100 * Math.pow(2, attempts - 1); - const start = Date.now(); - while (Date.now() - start < delay) { - // Busy wait - } - } - } + rmSync(this.tempDir, { recursive: true, force: true }); + mkdirSync(this.tempDir, { recursive: true }); } } From f55e035b40fa7856a677768c5afd06a543d7812d Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 23:31:06 -0800 Subject: [PATCH 60/69] attempted reset --- ...-8feae847-a6df-438f-babf-60e6ecb351c5.json | 7 - ...-7b4bdb9f-c16e-4bda-9459-b3b7c30755ab.json | 7 - ...-acf99833-5132-498d-9ef1-abcc8ee5a656.json | 7 - ...-c84f263a-964a-47d9-9a5c-337406e22fa5.json | 7 - ...-16ca2224-8177-4ede-ba6a-91d9c817054f.json | 7 - ...-1e20ce18-54fc-412a-bcaa-65ecabde4e57.json | 7 - package.json | 7 +- .../react-dialog/library/.swcrc | 3 + .../react-components/react-jsx-runtime/.swcrc | 3 + .../react-tree/library/.swcrc | 3 + .../react-components/react-utilities/.swcrc | 5 +- .../tsconfig.lib.json | 1 - .../tsconfig.lib.json | 1 - .../tsconfig.lib.json | 1 - .../generators/tsconfig-base-all/lib/utils.ts | 5 +- yarn.lock | 342 +++++------------- 16 files changed, 108 insertions(+), 305 deletions(-) delete mode 100644 change/@fluentui-react-dialog-8feae847-a6df-438f-babf-60e6ecb351c5.json delete mode 100644 change/@fluentui-react-jsx-runtime-7b4bdb9f-c16e-4bda-9459-b3b7c30755ab.json delete mode 100644 change/@fluentui-react-shared-contexts-acf99833-5132-498d-9ef1-abcc8ee5a656.json delete mode 100644 change/@fluentui-react-tree-c84f263a-964a-47d9-9a5c-337406e22fa5.json delete mode 100644 change/@fluentui-react-utilities-16ca2224-8177-4ede-ba6a-91d9c817054f.json delete mode 100644 change/@fluentui-storybook-llms-extractor-1e20ce18-54fc-412a-bcaa-65ecabde4e57.json diff --git a/change/@fluentui-react-dialog-8feae847-a6df-438f-babf-60e6ecb351c5.json b/change/@fluentui-react-dialog-8feae847-a6df-438f-babf-60e6ecb351c5.json deleted file mode 100644 index 5fa0fefdde37fd..00000000000000 --- a/change/@fluentui-react-dialog-8feae847-a6df-438f-babf-60e6ecb351c5.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "patch", - "comment": "plugin swc-plugin-de-indent-template-literal is crashing, removing", - "packageName": "@fluentui/react-dialog", - "email": "caperez@microsoft.com", - "dependentChangeType": "patch" -} diff --git a/change/@fluentui-react-jsx-runtime-7b4bdb9f-c16e-4bda-9459-b3b7c30755ab.json b/change/@fluentui-react-jsx-runtime-7b4bdb9f-c16e-4bda-9459-b3b7c30755ab.json deleted file mode 100644 index cb9eaed66d38ea..00000000000000 --- a/change/@fluentui-react-jsx-runtime-7b4bdb9f-c16e-4bda-9459-b3b7c30755ab.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "patch", - "comment": "plugin swc-plugin-de-indent-template-literal is crashing, removing", - "packageName": "@fluentui/react-jsx-runtime", - "email": "caperez@microsoft.com", - "dependentChangeType": "patch" -} diff --git a/change/@fluentui-react-shared-contexts-acf99833-5132-498d-9ef1-abcc8ee5a656.json b/change/@fluentui-react-shared-contexts-acf99833-5132-498d-9ef1-abcc8ee5a656.json deleted file mode 100644 index f37e335fe31030..00000000000000 --- a/change/@fluentui-react-shared-contexts-acf99833-5132-498d-9ef1-abcc8ee5a656.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "minor", - "comment": "Adding react-file-type-icons package for FluentUI v9", - "packageName": "@fluentui/react-shared-contexts", - "email": "caperez@microsoft.com", - "dependentChangeType": "patch" -} diff --git a/change/@fluentui-react-tree-c84f263a-964a-47d9-9a5c-337406e22fa5.json b/change/@fluentui-react-tree-c84f263a-964a-47d9-9a5c-337406e22fa5.json deleted file mode 100644 index 020e81b21ce7e7..00000000000000 --- a/change/@fluentui-react-tree-c84f263a-964a-47d9-9a5c-337406e22fa5.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "patch", - "comment": "plugin swc-plugin-de-indent-template-literal is crashing, removing", - "packageName": "@fluentui/react-tree", - "email": "caperez@microsoft.com", - "dependentChangeType": "patch" -} diff --git a/change/@fluentui-react-utilities-16ca2224-8177-4ede-ba6a-91d9c817054f.json b/change/@fluentui-react-utilities-16ca2224-8177-4ede-ba6a-91d9c817054f.json deleted file mode 100644 index 953eb2d68a01d6..00000000000000 --- a/change/@fluentui-react-utilities-16ca2224-8177-4ede-ba6a-91d9c817054f.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "minor", - "comment": "Adding react-file-type-icons package for FluentUI v9", - "packageName": "@fluentui/react-utilities", - "email": "caperez@microsoft.com", - "dependentChangeType": "patch" -} diff --git a/change/@fluentui-storybook-llms-extractor-1e20ce18-54fc-412a-bcaa-65ecabde4e57.json b/change/@fluentui-storybook-llms-extractor-1e20ce18-54fc-412a-bcaa-65ecabde4e57.json deleted file mode 100644 index 8f4167c2d18c7b..00000000000000 --- a/change/@fluentui-storybook-llms-extractor-1e20ce18-54fc-412a-bcaa-65ecabde4e57.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "none", - "comment": "fixing compilation config issues", - "packageName": "@fluentui/storybook-llms-extractor", - "email": "caperez@microsoft.com", - "dependentChangeType": "none" -} diff --git a/package.json b/package.json index 8b22087fbb8144..9b582500103a8a 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,8 @@ "@babel/plugin-proposal-class-properties": "7.18.6", "@babel/plugin-proposal-decorators": "7.24.6", "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6", - "@babel/plugin-proposal-object-rest-spread": "7.20.7", "@babel/plugin-proposal-optional-chaining": "7.21.0", + "@babel/plugin-proposal-object-rest-spread": "7.20.7", "@babel/plugin-syntax-dynamic-import": "7.8.3", "@babel/plugin-syntax-object-rest-spread": "7.8.3", "@babel/plugin-transform-runtime": "7.24.6", @@ -106,7 +106,7 @@ "@storybook/react-webpack5": "8.6.14", "@storybook/theming": "8.6.14", "@swc/cli": "0.7.7", - "@swc/core": "1.15.3", + "@swc/core": "1.11.24", "@swc/helpers": "0.5.1", "@swc/jest": "0.2.38", "@testing-library/dom": "10.4.0", @@ -166,8 +166,8 @@ "@types/webpack-hot-middleware": "2.25.9", "@types/yargs": "13.0.11", "@types/yargs-unparser": "2.0.1", - "@typescript-eslint/eslint-plugin": "^8.46.2", "@typescript-eslint/rule-tester": "8.46.2", + "@typescript-eslint/eslint-plugin": "^8.46.2", "autoprefixer": "10.2.1", "babel-jest": "29.7.0", "babel-loader": "9.1.3", @@ -338,7 +338,6 @@ "dependencies": { "@fluentui/react-icons-northstar": "0.66.5", "@fluentui/react-northstar": "0.66.5", - "@swc-node/register": "1.11.1", "copy-to-clipboard": "3.3.1" }, "license": "MIT", diff --git a/packages/react-components/react-dialog/library/.swcrc b/packages/react-components/react-dialog/library/.swcrc index b4ffa86dee3067..7c60535c03e245 100644 --- a/packages/react-components/react-dialog/library/.swcrc +++ b/packages/react-components/react-dialog/library/.swcrc @@ -10,6 +10,9 @@ "/**/*.test.tsx" ], "jsc": { + "experimental": { + "plugins": [["swc-plugin-de-indent-template-literal", {}]] + }, "parser": { "syntax": "typescript", "tsx": true, diff --git a/packages/react-components/react-jsx-runtime/.swcrc b/packages/react-components/react-jsx-runtime/.swcrc index b4ffa86dee3067..7c60535c03e245 100644 --- a/packages/react-components/react-jsx-runtime/.swcrc +++ b/packages/react-components/react-jsx-runtime/.swcrc @@ -10,6 +10,9 @@ "/**/*.test.tsx" ], "jsc": { + "experimental": { + "plugins": [["swc-plugin-de-indent-template-literal", {}]] + }, "parser": { "syntax": "typescript", "tsx": true, diff --git a/packages/react-components/react-tree/library/.swcrc b/packages/react-components/react-tree/library/.swcrc index b4ffa86dee3067..7c60535c03e245 100644 --- a/packages/react-components/react-tree/library/.swcrc +++ b/packages/react-components/react-tree/library/.swcrc @@ -10,6 +10,9 @@ "/**/*.test.tsx" ], "jsc": { + "experimental": { + "plugins": [["swc-plugin-de-indent-template-literal", {}]] + }, "parser": { "syntax": "typescript", "tsx": true, diff --git a/packages/react-components/react-utilities/.swcrc b/packages/react-components/react-utilities/.swcrc index 1f90e743a01739..7c60535c03e245 100644 --- a/packages/react-components/react-utilities/.swcrc +++ b/packages/react-components/react-utilities/.swcrc @@ -1,5 +1,5 @@ { - "$schema": "https://swc.rs/schema.json", + "$schema": "https://json.schemastore.org/swcrc", "exclude": [ "/testing", "/**/*.cy.ts", @@ -10,6 +10,9 @@ "/**/*.test.tsx" ], "jsc": { + "experimental": { + "plugins": [["swc-plugin-de-indent-template-literal", {}]] + }, "parser": { "syntax": "typescript", "tsx": true, diff --git a/tools/react-integration-tester/tsconfig.lib.json b/tools/react-integration-tester/tsconfig.lib.json index 71bb06dd725935..33eca2c2cdf8c6 100644 --- a/tools/react-integration-tester/tsconfig.lib.json +++ b/tools/react-integration-tester/tsconfig.lib.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "rootDir": "src", "outDir": "../../dist/out-tsc", "declaration": true, "types": ["node"] diff --git a/tools/storybook-llms-extractor/tsconfig.lib.json b/tools/storybook-llms-extractor/tsconfig.lib.json index 3bcfb1fccd2c99..0e55672051597b 100644 --- a/tools/storybook-llms-extractor/tsconfig.lib.json +++ b/tools/storybook-llms-extractor/tsconfig.lib.json @@ -2,7 +2,6 @@ "extends": "./tsconfig.json", "compilerOptions": { "module": "CommonJS", - "rootDir": "src", "noEmit": false, "lib": ["ES2020", "DOM"], "declaration": true, diff --git a/tools/visual-regression-assert/tsconfig.lib.json b/tools/visual-regression-assert/tsconfig.lib.json index 27ed0f811a1f10..e8e07a7a01fe7b 100644 --- a/tools/visual-regression-assert/tsconfig.lib.json +++ b/tools/visual-regression-assert/tsconfig.lib.json @@ -2,7 +2,6 @@ "extends": "./tsconfig.json", "compilerOptions": { "module": "CommonJS", - "rootDir": "src", "noEmit": false, "lib": ["ES2020"], "declaration": true, diff --git a/tools/workspace-plugin/src/generators/tsconfig-base-all/lib/utils.ts b/tools/workspace-plugin/src/generators/tsconfig-base-all/lib/utils.ts index 62382e3da8517b..9bc9c3e0730a39 100644 --- a/tools/workspace-plugin/src/generators/tsconfig-base-all/lib/utils.ts +++ b/tools/workspace-plugin/src/generators/tsconfig-base-all/lib/utils.ts @@ -1,4 +1,5 @@ // use this module to define any kind of generic utilities that are used in more than 1 place within the generator implementation +import path from 'path'; import { readJson, Tree } from '@nx/devkit'; /** @@ -10,8 +11,8 @@ export function createPathAliasesConfig(tree: Tree) { const existingTsConfig = tree.exists(tsConfigAllPath) ? readJson(tree, tsConfigAllPath) : null; const baseConfigs = { - v8: readJson(tree, '/tsconfig.base.v8.json'), - v9: readJson(tree, '/tsconfig.base.json'), + v8: readJson(tree, path.join('/tsconfig.base.v8.json')), + v9: readJson(tree, path.join('/tsconfig.base.json')), }; const tsConfigBase = '.'; const mergedTsConfig = { diff --git a/yarn.lock b/yarn.lock index b6a6323af9ef4c..dc554e366e92ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1581,25 +1581,25 @@ "@effect/io" "^0.26.0" fast-check "^3.10.0" -"@emnapi/core@^1.1.0", "@emnapi/core@^1.5.0": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.7.1.tgz#3a79a02dbc84f45884a1806ebb98e5746bdfaac4" - integrity sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg== +"@emnapi/core@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.4.0.tgz#8844b02d799198158ac1fea21ae2bc81b881da9a" + integrity sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg== dependencies: - "@emnapi/wasi-threads" "1.1.0" + "@emnapi/wasi-threads" "1.0.1" tslib "^2.4.0" -"@emnapi/runtime@^1.1.0", "@emnapi/runtime@^1.5.0": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.7.1.tgz#a73784e23f5d57287369c808197288b52276b791" - integrity sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA== +"@emnapi/runtime@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.4.0.tgz#8f509bf1059a5551c8fe829a1c4e91db35fdfbee" + integrity sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw== dependencies: tslib "^2.4.0" -"@emnapi/wasi-threads@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf" - integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ== +"@emnapi/wasi-threads@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz#d7ae71fd2166b1c916c6cd2d0df2ef565a2e1a5b" + integrity sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw== dependencies: tslib "^2.4.0" @@ -2828,15 +2828,6 @@ "@emnapi/runtime" "^1.1.0" "@tybys/wasm-util" "^0.9.0" -"@napi-rs/wasm-runtime@^1.0.7": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz#dcfea99a75f06209a235f3d941e3460a51e9b14c" - integrity sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw== - dependencies: - "@emnapi/core" "^1.5.0" - "@emnapi/runtime" "^1.5.0" - "@tybys/wasm-util" "^0.10.1" - "@nevware21/ts-async@>= 0.5.2 < 2.x": version "0.5.2" resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.5.2.tgz#a41883dc6ccc4666bdf156e92f35f3003fd3f6f0" @@ -3169,103 +3160,6 @@ css-tree "^3.0.0" nanoid "^5.0.8" -"@oxc-resolver/binding-android-arm-eabi@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.14.0.tgz#98a6b41316d9283c89dac4261ef2b3d3458943ec" - integrity sha512-jB47iZ/thvhE+USCLv+XY3IknBbkKr/p7OBsQDTHode/GPw+OHRlit3NQ1bjt1Mj8V2CS7iHdSDYobZ1/0gagQ== - -"@oxc-resolver/binding-android-arm64@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.14.0.tgz#1a2f24785dc4b8f86eebe5873f0fcd8ba850fd54" - integrity sha512-XFJ9t7d/Cz+dWLyqtTy3Xrekz+qqN4hmOU2iOUgr7u71OQsPUHIIeS9/wKanEK0l413gPwapIkyc5x9ltlOtyw== - -"@oxc-resolver/binding-darwin-arm64@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.14.0.tgz#67cb9b66b060e9a29f6c8d9549669fa061ff6006" - integrity sha512-gwehBS9smA1mzK8frDsmUCHz+6baJVwkKF6qViHhoqA3kRKvIZ3k6WNP4JmF19JhOiGxRcoPa8gZRfzNgXwP2A== - -"@oxc-resolver/binding-darwin-x64@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.14.0.tgz#b7ab6433a6babc8df133497edd847894d3b10061" - integrity sha512-5wwJvfuoahKiAqqAsMLOI28rqdh3P2K7HkjIWUXNMWAZq6ErX0L5rwJzu6T32+Zxw3k18C7R9IS4wDq/3Ar+6w== - -"@oxc-resolver/binding-freebsd-x64@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.14.0.tgz#a7910fc9ad69c12ba227a57580b2053cffc623f0" - integrity sha512-MWTt+LOQNcQ6fa+Uu5VikkihLi1PSIrQqqp0QD44k2AORasNWl0jRGBTcMSBIgNe82qEQWYvlGzvOEEOBp01Og== - -"@oxc-resolver/binding-linux-arm-gnueabihf@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.14.0.tgz#c06d530315f4df3151f44ee3d10488a426fee359" - integrity sha512-b6/IBqYrS3o0XiLVBsnex/wK8pTTK+hbGfAMOHVU6p7DBpwPPLgC/tav4IXoOIUCssTFz7aWh/xtUok0swn8VQ== - -"@oxc-resolver/binding-linux-arm-musleabihf@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.14.0.tgz#50be40fb4a4cdbbb600198bfc9a1f3a6f4233d16" - integrity sha512-o2Qh5+y5YoqVK6YfzkalHdpmQ5bkbGGxuLg1pZLQ1Ift0x+Vix7DaFEpdCl5Z9xvYXogd/TwOlL0TPl4+MTFLA== - -"@oxc-resolver/binding-linux-arm64-gnu@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.14.0.tgz#62fc245a4e83bd2d932de5865cdea3240cf08097" - integrity sha512-lk8mCSg0Tg4sEG73RiPjb7keGcEPwqQnBHX3Z+BR2SWe+qNHpoHcyFMNafzSvEC18vlxC04AUSoa6kJl/C5zig== - -"@oxc-resolver/binding-linux-arm64-musl@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.14.0.tgz#c90bfd0dde8466f982fed7e9332b39c60ffbe682" - integrity sha512-KykeIVhCM7pn93ABa0fNe8vk4XvnbfZMELne2s6P9tdJH9KMBsCFBi7a2BmSdUtTqWCAJokAcm46lpczU52Xaw== - -"@oxc-resolver/binding-linux-ppc64-gnu@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.14.0.tgz#a9fc775ec2db68761df8437ec11b015c46741b34" - integrity sha512-QqPPWAcZU/jHAuam4f3zV8OdEkYRPD2XR0peVet3hoMMgsihR3Lhe7J/bLclmod297FG0+OgBYQVMh2nTN6oWA== - -"@oxc-resolver/binding-linux-riscv64-gnu@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.14.0.tgz#8002c80ba035e26d476fbad50e0b149b9a16079c" - integrity sha512-DunWA+wafeG3hj1NADUD3c+DRvmyVNqF5LSHVUWA2bzswqmuEZXl3VYBSzxfD0j+UnRTFYLxf27AMptoMsepYg== - -"@oxc-resolver/binding-linux-riscv64-musl@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.14.0.tgz#6a3c20f55295626572be80de82b4b4a927d5b073" - integrity sha512-4SRvwKTTk2k67EQr9Ny4NGf/BhlwggCI1CXwBbA9IV4oP38DH8b+NAPxDY0ySGRsWbPkG92FYOqM4AWzG4GSgA== - -"@oxc-resolver/binding-linux-s390x-gnu@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.14.0.tgz#c8031033466a64206ce59bbc771c43c135843495" - integrity sha512-hZKvkbsurj4JOom//R1Ab2MlC4cGeVm5zzMt4IsS3XySQeYjyMJ5TDZ3J5rQ8bVj3xi4FpJU2yFZ72GApsHQ6A== - -"@oxc-resolver/binding-linux-x64-gnu@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.14.0.tgz#7ef94d565dd4be964df1b80e34b515339652f358" - integrity sha512-hABxQXFXJurivw+0amFdeEcK67cF1BGBIN1+sSHzq3TRv4RoG8n5q2JE04Le2n2Kpt6xg4Y5+lcv+rb2mCJLgQ== - -"@oxc-resolver/binding-linux-x64-musl@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.14.0.tgz#2d4226bc06ad336f4854dfd3709f566c02683da6" - integrity sha512-Ln73wUB5migZRvC7obAAdqVwvFvk7AUs2JLt4g9QHr8FnqivlsjpUC9Nf2ssrybdjyQzEMjttUxPZz6aKPSAHw== - -"@oxc-resolver/binding-wasm32-wasi@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.14.0.tgz#c8a048253fa58e239b69f89734d954a631434a9e" - integrity sha512-z+NbELmCOKNtWOqEB5qDfHXOSWB3kGQIIehq6nHtZwHLzdVO2oBq6De/ayhY3ygriC1XhgaIzzniY7jgrNl4Kw== - dependencies: - "@napi-rs/wasm-runtime" "^1.0.7" - -"@oxc-resolver/binding-win32-arm64-msvc@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.14.0.tgz#b0cdf4f7ad4a58995b6b538c844b5b9a2efb7edb" - integrity sha512-Ft0+qd7HSO61qCTLJ4LCdBGZkpKyDj1rG0OVSZL1DxWQoh97m7vEHd7zAvUtw8EcWjOMBQuX4mfRap/x2MOCpQ== - -"@oxc-resolver/binding-win32-ia32-msvc@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.14.0.tgz#616a641967db9fcacebdc06443fad29dcda70a42" - integrity sha512-o54jYNSfGdPxHSvXEhZg8FOV3K99mJ1f7hb1alRFb+Yec1GQXNrJXxZPIxNMYeFT13kwAWB7zuQ0HZLnDHFxfw== - -"@oxc-resolver/binding-win32-x64-msvc@11.14.0": - version "11.14.0" - resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.14.0.tgz#53e9a5257332da71cce15f09e42e3b1927156370" - integrity sha512-j97icaORyM6A7GjgmUzfn7V+KGzVvctRA+eAlJb0c2OQNaETFxl6BXZdnGBDb+6oA0Y4Sr/wnekd1kQ0aVyKGg== - "@phenomnomnominal/tsquery@6.1.3", "@phenomnomnominal/tsquery@~5.0.1": version "6.1.3" resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-6.1.3.tgz#5e819403da2fa6a64b009f1876278fb105ec6b55" @@ -4048,32 +3942,6 @@ "@types/express" "^4.7.0" file-system-cache "2.3.0" -"@swc-node/core@^1.14.1": - version "1.14.1" - resolved "https://registry.yarnpkg.com/@swc-node/core/-/core-1.14.1.tgz#b3f85f9ac055faee1a3454a0cf71bb953c814f1a" - integrity sha512-jrt5GUaZUU6cmMS+WTJEvGvaB6j1YNKPHPzC2PUi2BjaFbtxURHj6641Az6xN7b665hNniAIdvjxWcRml5yCnw== - -"@swc-node/register@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@swc-node/register/-/register-1.11.1.tgz#bc353f9df7d9c6f0e7f067fe2b3a7552fd667698" - integrity sha512-VQ0hJ5jX31TVv/fhZx4xJRzd8pwn6VvzYd2tGOHHr2TfXGCBixZoqdPDXTiEoJLCTS2MmvBf6zyQZZ0M8aGQCQ== - dependencies: - "@swc-node/core" "^1.14.1" - "@swc-node/sourcemap-support" "^0.6.1" - colorette "^2.0.20" - debug "^4.4.1" - oxc-resolver "^11.6.1" - pirates "^4.0.7" - tslib "^2.8.1" - -"@swc-node/sourcemap-support@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@swc-node/sourcemap-support/-/sourcemap-support-0.6.1.tgz#579bd0cfebc9cea51dd0de7c59c47ecd07b9e505" - integrity sha512-ovltDVH5QpdHXZkW138vG4+dgcNsxfwxHVoV6BtmTbz2KKl1A8ZSlbdtxzzfNjCjbpayda8Us9eMtcHobm38dA== - dependencies: - source-map-support "^0.5.21" - tslib "^2.8.1" - "@swc/cli@0.7.7": version "0.7.7" resolved "https://registry.yarnpkg.com/@swc/cli/-/cli-0.7.7.tgz#b367daba7db5a25fdcdbefe6a80f4c49c300d5fc" @@ -4089,74 +3957,74 @@ slash "3.0.0" source-map "^0.7.3" -"@swc/core-darwin-arm64@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.3.tgz#bd0bd3ab7730e3ffa64cf200c0ed7c572cbaba97" - integrity sha512-AXfeQn0CvcQ4cndlIshETx6jrAM45oeUrK8YeEY6oUZU/qzz0Id0CyvlEywxkWVC81Ajpd8TQQ1fW5yx6zQWkQ== - -"@swc/core-darwin-x64@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.15.3.tgz#502b1e1c680df6b962265ca81a0c1a23e6ff070f" - integrity sha512-p68OeCz1ui+MZYG4wmfJGvcsAcFYb6Sl25H9TxWl+GkBgmNimIiRdnypK9nBGlqMZAcxngNPtnG3kEMNnvoJ2A== - -"@swc/core-linux-arm-gnueabihf@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.3.tgz#e32cc6a2e06a75060d6f598ba2ca6f96c5c0cc43" - integrity sha512-Nuj5iF4JteFgwrai97mUX+xUOl+rQRHqTvnvHMATL/l9xE6/TJfPBpd3hk/PVpClMXG3Uvk1MxUFOEzM1JrMYg== - -"@swc/core-linux-arm64-gnu@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.3.tgz#9b9861bc44059e393d4baf98b3cd3d6c4ea6f521" - integrity sha512-2Nc/s8jE6mW2EjXWxO/lyQuLKShcmTrym2LRf5Ayp3ICEMX6HwFqB1EzDhwoMa2DcUgmnZIalesq2lG3krrUNw== - -"@swc/core-linux-arm64-musl@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.3.tgz#f6388743e5a159018bd468e8f710940b2614384b" - integrity sha512-j4SJniZ/qaZ5g8op+p1G9K1z22s/EYGg1UXIb3+Cg4nsxEpF5uSIGEE4mHUfA70L0BR9wKT2QF/zv3vkhfpX4g== - -"@swc/core-linux-x64-gnu@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.3.tgz#15fea551c7a3aeb1bdc3ad5c652d73c9321ddba8" - integrity sha512-aKttAZnz8YB1VJwPQZtyU8Uk0BfMP63iDMkvjhJzRZVgySmqt/apWSdnoIcZlUoGheBrcqbMC17GGUmur7OT5A== - -"@swc/core-linux-x64-musl@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.3.tgz#d3f17bab4ffcadbb47f135e6a14d6f3e401af289" - integrity sha512-oe8FctPu1gnUsdtGJRO2rvOUIkkIIaHqsO9xxN0bTR7dFTlPTGi2Fhk1tnvXeyAvCPxLIcwD8phzKg6wLv9yug== - -"@swc/core-win32-arm64-msvc@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.3.tgz#9da386df7fed00b3473bcf4281ff3fcd14726d2c" - integrity sha512-L9AjzP2ZQ/Xh58e0lTRMLvEDrcJpR7GwZqAtIeNLcTK7JVE+QineSyHp0kLkO1rttCHyCy0U74kDTj0dRz6raA== - -"@swc/core-win32-ia32-msvc@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.3.tgz#c398d4f0f10ffec2151a79733ee1ce86a945a1ea" - integrity sha512-B8UtogMzErUPDWUoKONSVBdsgKYd58rRyv2sHJWKOIMCHfZ22FVXICR4O/VwIYtlnZ7ahERcjayBHDlBZpR0aw== - -"@swc/core-win32-x64-msvc@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.3.tgz#715596b034a654c82b03ef734a9b44c29bcd3a68" - integrity sha512-SpZKMR9QBTecHeqpzJdYEfgw30Oo8b/Xl6rjSzBt1g0ZsXyy60KLXrp6IagQyfTYqNYE/caDvwtF2FPn7pomog== - -"@swc/core@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.15.3.tgz#2d0a5c4ac4c180c3dbf2f6d5d958b9fcbaa9755f" - integrity sha512-Qd8eBPkUFL4eAONgGjycZXj1jFCBW8Fd+xF0PzdTlBCWQIV1xnUT7B93wUANtW3KGjl3TRcOyxwSx/u/jyKw/Q== +"@swc/core-darwin-arm64@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.24.tgz#c9fcc9c4bad0511fed26210449556d2b33fb2d9a" + integrity sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA== + +"@swc/core-darwin-x64@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.11.24.tgz#048ea3ee43281264a62fccb5a944b76d1c56eb24" + integrity sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ== + +"@swc/core-linux-arm-gnueabihf@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.24.tgz#f01ba657a81c67d8fb9f681712e65abf1324cec6" + integrity sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw== + +"@swc/core-linux-arm64-gnu@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.24.tgz#9aefca7f7f87c8312c2fa714c1eb731411d8596c" + integrity sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg== + +"@swc/core-linux-arm64-musl@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.24.tgz#e4805484779bbc59b639eab4f8e45166f3d7a4f7" + integrity sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw== + +"@swc/core-linux-x64-gnu@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.24.tgz#e8d8cc50a49903880944379590b73733e150a5d4" + integrity sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg== + +"@swc/core-linux-x64-musl@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.24.tgz#f3c46212eb8a793f6a42a36b2a9017a9b1462737" + integrity sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw== + +"@swc/core-win32-arm64-msvc@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.24.tgz#b1c3327d81a5f94415ac0b1713e192df1c121fbd" + integrity sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ== + +"@swc/core-win32-ia32-msvc@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.24.tgz#6a944dd6111ec5fae3cf5925b73701e49b109edc" + integrity sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ== + +"@swc/core-win32-x64-msvc@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.24.tgz#eebb5d5ece2710aeb25cc58bd7c5c4c2c046f030" + integrity sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w== + +"@swc/core@1.11.24": + version "1.11.24" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.11.24.tgz#340425648296964f815c940b8da00fcdb1ff2abd" + integrity sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg== dependencies: "@swc/counter" "^0.1.3" - "@swc/types" "^0.1.25" + "@swc/types" "^0.1.21" optionalDependencies: - "@swc/core-darwin-arm64" "1.15.3" - "@swc/core-darwin-x64" "1.15.3" - "@swc/core-linux-arm-gnueabihf" "1.15.3" - "@swc/core-linux-arm64-gnu" "1.15.3" - "@swc/core-linux-arm64-musl" "1.15.3" - "@swc/core-linux-x64-gnu" "1.15.3" - "@swc/core-linux-x64-musl" "1.15.3" - "@swc/core-win32-arm64-msvc" "1.15.3" - "@swc/core-win32-ia32-msvc" "1.15.3" - "@swc/core-win32-x64-msvc" "1.15.3" + "@swc/core-darwin-arm64" "1.11.24" + "@swc/core-darwin-x64" "1.11.24" + "@swc/core-linux-arm-gnueabihf" "1.11.24" + "@swc/core-linux-arm64-gnu" "1.11.24" + "@swc/core-linux-arm64-musl" "1.11.24" + "@swc/core-linux-x64-gnu" "1.11.24" + "@swc/core-linux-x64-musl" "1.11.24" + "@swc/core-win32-arm64-msvc" "1.11.24" + "@swc/core-win32-ia32-msvc" "1.11.24" + "@swc/core-win32-x64-msvc" "1.11.24" "@swc/counter@^0.1.3": version "0.1.3" @@ -4179,7 +4047,7 @@ "@swc/counter" "^0.1.3" jsonc-parser "^3.2.0" -"@swc/types@^0.1.25": +"@swc/types@^0.1.21": version "0.1.25" resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.25.tgz#b517b2a60feb37dd933e542d93093719e4cf1078" integrity sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g== @@ -4378,13 +4246,6 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node20/-/node20-20.1.6.tgz#cdf11db8322e1c245d5a4bb2e398239c82ae78b2" integrity sha512-sz+Hqx9zwZDpZIV871WSbUzSqNIsXzghZydypnfgzPKLltVJfkINfUeTct31n/tTSa9ZE1ZOfKdRre1uHHquYQ== -"@tybys/wasm-util@^0.10.1": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414" - integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== - dependencies: - tslib "^2.4.0" - "@tybys/wasm-util@^0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.9.0.tgz#3e75eb00604c8d6db470bf18c37b7d984a0e3355" @@ -7736,7 +7597,7 @@ colorette@^1.2.1: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== -colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.16, colorette@^2.0.20: +colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.16: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -8557,10 +8418,10 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@^4.3.7, debug@^4.4.1: - version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@^4.3.7: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== dependencies: ms "^2.1.3" @@ -16194,31 +16055,6 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" -oxc-resolver@^11.6.1: - version "11.14.0" - resolved "https://registry.yarnpkg.com/oxc-resolver/-/oxc-resolver-11.14.0.tgz#8737deba9b8cd51054d56fb9886213d1fa7cf5a5" - integrity sha512-i4wNrqhOd+4YdHJfHglHtFiqqSxXuzFA+RUqmmWN1aMD3r1HqUSrIhw17tSO4jwKfhLs9uw1wzFPmvMsWacStg== - optionalDependencies: - "@oxc-resolver/binding-android-arm-eabi" "11.14.0" - "@oxc-resolver/binding-android-arm64" "11.14.0" - "@oxc-resolver/binding-darwin-arm64" "11.14.0" - "@oxc-resolver/binding-darwin-x64" "11.14.0" - "@oxc-resolver/binding-freebsd-x64" "11.14.0" - "@oxc-resolver/binding-linux-arm-gnueabihf" "11.14.0" - "@oxc-resolver/binding-linux-arm-musleabihf" "11.14.0" - "@oxc-resolver/binding-linux-arm64-gnu" "11.14.0" - "@oxc-resolver/binding-linux-arm64-musl" "11.14.0" - "@oxc-resolver/binding-linux-ppc64-gnu" "11.14.0" - "@oxc-resolver/binding-linux-riscv64-gnu" "11.14.0" - "@oxc-resolver/binding-linux-riscv64-musl" "11.14.0" - "@oxc-resolver/binding-linux-s390x-gnu" "11.14.0" - "@oxc-resolver/binding-linux-x64-gnu" "11.14.0" - "@oxc-resolver/binding-linux-x64-musl" "11.14.0" - "@oxc-resolver/binding-wasm32-wasi" "11.14.0" - "@oxc-resolver/binding-win32-arm64-msvc" "11.14.0" - "@oxc-resolver/binding-win32-ia32-msvc" "11.14.0" - "@oxc-resolver/binding-win32-x64-msvc" "11.14.0" - p-cancelable@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" @@ -16736,10 +16572,10 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pirates@^4.0.4, pirates@^4.0.6, pirates@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" - integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== +pirates@^4.0.4, pirates@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== piscina@^3.2.0: version "3.2.0" @@ -18715,7 +18551,7 @@ source-map-support@0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@~0.5.12, source-map-support@~0.5.20: +source-map-support@^0.5.16, source-map-support@~0.5.12, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== From 15c87a6f8deb14babe5cf763b151e0b7c5e32ac8 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Tue, 25 Nov 2025 23:34:29 -0800 Subject: [PATCH 61/69] yarn change in shared-contexts --- ...ared-contexts-8bdf4419-527c-4852-accd-686067675ba9.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-shared-contexts-8bdf4419-527c-4852-accd-686067675ba9.json diff --git a/change/@fluentui-react-shared-contexts-8bdf4419-527c-4852-accd-686067675ba9.json b/change/@fluentui-react-shared-contexts-8bdf4419-527c-4852-accd-686067675ba9.json new file mode 100644 index 00000000000000..0c3fc823a60a83 --- /dev/null +++ b/change/@fluentui-react-shared-contexts-8bdf4419-527c-4852-accd-686067675ba9.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "including package react-file-type-icons v9", + "packageName": "@fluentui/react-shared-contexts", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} From 921aeff9c98aa852c1852ebf10f530ed7d5f5d8c Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Wed, 26 Nov 2025 10:30:37 -0800 Subject: [PATCH 62/69] chore: update package dependencies for react and charting components --- packages/public-docsite-setup/package.json | 3 +-- packages/react-examples/package.json | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/public-docsite-setup/package.json b/packages/public-docsite-setup/package.json index 6959a27adc4fce..ead0980295c33e 100644 --- a/packages/public-docsite-setup/package.json +++ b/packages/public-docsite-setup/package.json @@ -26,6 +26,5 @@ "devDependencies": { "@fluentui/eslint-plugin": "*", "@fluentui/scripts-tasks": "*" - }, - "dependencies": {} + } } diff --git a/packages/react-examples/package.json b/packages/react-examples/package.json index 2a80d668b2d90b..813f858f189340 100644 --- a/packages/react-examples/package.json +++ b/packages/react-examples/package.json @@ -31,13 +31,13 @@ "@fluentui/font-icons-mdl2": "^8.5.69", "@fluentui/foundation-legacy": "^8.6.2", "@fluentui/merge-styles": "^8.6.14", - "@fluentui/react": "^8.125.1", + "@fluentui/react": "^8.125.2", "@fluentui/react-cards": "^0.207.1", - "@fluentui/react-charting": "^5.25.2", + "@fluentui/react-charting": "^5.25.3", "@fluentui/react-docsite-components": "^8.15.0", "@fluentui/react-experiments": "^8.16.1", "@fluentui/react-file-type-icons-v8": "^8.15.1", - "@fluentui/react-focus": "^8.10.1", + "@fluentui/react-focus": "^8.10.2", "@fluentui/react-hooks": "^8.10.1", "@fluentui/react-icons-mdl2": "^1.4.2", "@fluentui/react-window-provider": "^2.3.1", From 91ed548522a6a9ae600fe1fecb1d4b4c4525f3b6 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Wed, 26 Nov 2025 11:26:49 -0800 Subject: [PATCH 63/69] feat: add public docsite setup for v8 version of file-type-icons package --- ...docsite-setup-7ad0c9ff-7dd2-4ebe-a63f-a94d93a6b458.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-public-docsite-setup-7ad0c9ff-7dd2-4ebe-a63f-a94d93a6b458.json diff --git a/change/@fluentui-public-docsite-setup-7ad0c9ff-7dd2-4ebe-a63f-a94d93a6b458.json b/change/@fluentui-public-docsite-setup-7ad0c9ff-7dd2-4ebe-a63f-a94d93a6b458.json new file mode 100644 index 00000000000000..880087b79cce6d --- /dev/null +++ b/change/@fluentui-public-docsite-setup-7ad0c9ff-7dd2-4ebe-a63f-a94d93a6b458.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Associating this older public docsite with the v8 version of the file-type-icons package (new workspace name got -v8 suffix added)", + "packageName": "@fluentui/public-docsite-setup", + "email": "caperez@microsoft.com", + "dependentChangeType": "patch" +} From c7a138895e96fc81ddfdbaaf43b8a6c6644a6ab1 Mon Sep 17 00:00:00 2001 From: "C. Perez" Date: Mon, 15 Dec 2025 10:08:06 -0800 Subject: [PATCH 64/69] missing comma after merge --- packages/react-components/react-components/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-components/package.json b/packages/react-components/react-components/package.json index c3aad284b74b86..7f406dc5a0752f 100644 --- a/packages/react-components/react-components/package.json +++ b/packages/react-components/react-components/package.json @@ -77,7 +77,7 @@ "@fluentui/react-motion": "^9.11.4", "@fluentui/react-carousel": "^9.8.13", "@fluentui/react-color-picker": "^9.2.11", - "@fluentui/react-file-type-icons": "^9.0.0" + "@fluentui/react-file-type-icons": "^9.0.0", "@fluentui/react-nav": "^9.3.15" }, "peerDependencies": { From 741d4b300cc17f4d5cd170b2bbb4505ecb58093e Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Wed, 17 Dec 2025 14:43:54 -0800 Subject: [PATCH 65/69] fixing overwritten package name differentiation, retaining publishConfig to publish both v8.x.x and v9.x.x packages if needed --- packages/react-file-type-icons/package.json | 2 +- yarn.lock | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/react-file-type-icons/package.json b/packages/react-file-type-icons/package.json index c34a55c00b50a4..c406a8ea264ed7 100644 --- a/packages/react-file-type-icons/package.json +++ b/packages/react-file-type-icons/package.json @@ -1,5 +1,5 @@ { - "name": "@fluentui/react-file-type-icons", + "name": "@fluentui/react-file-type-icons-v8", "version": "8.15.3", "description": "Fluent UI React file type icon set for FluentUI v8", "main": "lib-commonjs/index.js", diff --git a/yarn.lock b/yarn.lock index 6ed78a738c5105..d8db6451202f24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1868,6 +1868,15 @@ "@babel/runtime" "^7.10.4" react-is "^17.0.2" +"@fluentui/react-file-type-icons@^8.15.3": + version "8.15.3" + resolved "https://registry.yarnpkg.com/@fluentui/react-file-type-icons/-/react-file-type-icons-8.15.3.tgz#04b8f5ac77b88e6b23083f7328be80bc9813f6bf" + integrity sha512-RT4gUmkg7UX9FUsG0zTwCNpkK2BmiU2dUVj/VAFm841WDDqMpsVeQutJZ5u+QVdyvEVZMLqUVVw33Rsd1cOHGg== + dependencies: + "@fluentui/set-version" "^8.2.24" + "@fluentui/style-utilities" "^8.13.6" + tslib "^2.1.0" + "@fluentui/react-icons-northstar@*", "@fluentui/react-icons-northstar@0.66.5", "@fluentui/react-icons-northstar@^0.66.5": version "0.66.5" resolved "https://registry.yarnpkg.com/@fluentui/react-icons-northstar/-/react-icons-northstar-0.66.5.tgz#a33765f1c3ac98e1901d9b208d78f42d1ad23cb5" From f06399eb2254f7c798de8f81505d47a38eadc298 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Wed, 17 Dec 2025 15:45:55 -0800 Subject: [PATCH 66/69] added correct config in syncpack semvergroups to support sim-shipping --- syncpack.config.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/syncpack.config.js b/syncpack.config.js index 6fbd804075c49f..60e179215bcdf4 100644 --- a/syncpack.config.js +++ b/syncpack.config.js @@ -17,6 +17,12 @@ const config = { dependencies: ['@fluentui/**'], isIgnored: true, }, + { + // Allow both v8 and v9 versions of @fluentui/react-file-type-icons to coexist + // v8 publishes as 8.x.x, v9 publishes as 9.x.x to the same npm package name + dependencies: ['@fluentui/react-file-type-icons'], + isIgnored: true, + }, ], versionGroups: [ // completely ignore all devDeps that specify inner workspace deps - as we enforce usage of `*` or `>9.0.0-alpha` From f98c7f1d32e0ff170dc88fadd59fe23e4f6cbcb7 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Wed, 17 Dec 2025 17:09:25 -0800 Subject: [PATCH 67/69] fixing syncpack config --- syncpack.config.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/syncpack.config.js b/syncpack.config.js index 60e179215bcdf4..46aed39341a96b 100644 --- a/syncpack.config.js +++ b/syncpack.config.js @@ -17,12 +17,6 @@ const config = { dependencies: ['@fluentui/**'], isIgnored: true, }, - { - // Allow both v8 and v9 versions of @fluentui/react-file-type-icons to coexist - // v8 publishes as 8.x.x, v9 publishes as 9.x.x to the same npm package name - dependencies: ['@fluentui/react-file-type-icons'], - isIgnored: true, - }, ], versionGroups: [ // completely ignore all devDeps that specify inner workspace deps - as we enforce usage of `*` or `>9.0.0-alpha` @@ -32,6 +26,13 @@ const config = { dependencies: ['@fluentui/**'], isIgnored: true, }, + { + // Allow both v8 and v9 versions of @fluentui/react-file-type-icons to coexist + // v8 publishes as 8.x.x, v9 publishes as 9.x.x to the same npm package name + packages: ['**'], + dependencies: ['@fluentui/react-file-type-icons'], + isIgnored: true, + }, { packages: ['@fluentui/fluentui-repo'], dependencies: [ From 85c4d999878b30da4a181ad82f209d676e4286c2 Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Wed, 17 Dec 2025 18:18:42 -0800 Subject: [PATCH 68/69] update package name for file type icons to v8 version --- packages/react-examples/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-examples/package.json b/packages/react-examples/package.json index 1ca886551586df..96e3c2bda516cd 100644 --- a/packages/react-examples/package.json +++ b/packages/react-examples/package.json @@ -36,7 +36,7 @@ "@fluentui/react-charting": "^5.25.4", "@fluentui/react-docsite-components": "^8.16.0", "@fluentui/react-experiments": "^8.16.3", - "@fluentui/react-file-type-icons": "^8.15.3", + "@fluentui/react-file-type-icons-v8": "^8.15.3", "@fluentui/react-focus": "^8.10.3", "@fluentui/react-hooks": "^8.10.2", "@fluentui/react-icons-mdl2": "^1.4.3", From 2b58691f6c85c046fbde4cc78873a193305b6d4f Mon Sep 17 00:00:00 2001 From: "Carlos Perez (M365)" Date: Wed, 17 Dec 2025 22:42:27 -0800 Subject: [PATCH 69/69] fixing name error - undoing --- packages/react-examples/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-examples/package.json b/packages/react-examples/package.json index 96e3c2bda516cd..1ca886551586df 100644 --- a/packages/react-examples/package.json +++ b/packages/react-examples/package.json @@ -36,7 +36,7 @@ "@fluentui/react-charting": "^5.25.4", "@fluentui/react-docsite-components": "^8.16.0", "@fluentui/react-experiments": "^8.16.3", - "@fluentui/react-file-type-icons-v8": "^8.15.3", + "@fluentui/react-file-type-icons": "^8.15.3", "@fluentui/react-focus": "^8.10.3", "@fluentui/react-hooks": "^8.10.2", "@fluentui/react-icons-mdl2": "^1.4.3",