From e18c7f08dc0d483db270e4d0b7d89081ee6940e7 Mon Sep 17 00:00:00 2001 From: Scott Thompson <145565743+scotttjob@users.noreply.github.com> Date: Fri, 17 Jan 2025 09:59:44 -0400 Subject: [PATCH 1/5] v1, issue with hovering --- .../components/src/FormatFile/FormatFile.tsx | 187 +++++++++--------- .../src/FormatFile/FormatFileDeleteButton.tsx | 36 ---- .../src/FormatFile/InternalThumbnail.tsx | 69 ------- .../InternalThumbnailImage.module.css | 20 -- .../InternalThumbnailImage.module.css.d.ts | 7 - .../src/FormatFile/InternalThumbnailImage.tsx | 38 ---- packages/components/src/FormatFile/types.ts | 39 ++++ .../src/FormatFile/useFormatFile.tsx | 12 ++ .../src/FormatFile/useFormatFileStyles.ts | 39 ++++ .../Thumbnail.module.css} | 21 ++ .../Thumbnail.module.css.d.ts} | 3 + .../Thumbnail.test.tsx} | 8 +- .../components/src/Thumbnail/Thumbnail.tsx | 61 ++++++ packages/components/src/Thumbnail/index.ts | 1 + packages/components/src/Thumbnail/types.ts | 7 + .../components/src/Thumbnail/useThumbnail.tsx | 36 ++++ .../src/Thumbnail/useThumbnailImage.tsx | 17 ++ 17 files changed, 335 insertions(+), 266 deletions(-) delete mode 100644 packages/components/src/FormatFile/FormatFileDeleteButton.tsx delete mode 100644 packages/components/src/FormatFile/InternalThumbnail.tsx delete mode 100644 packages/components/src/FormatFile/InternalThumbnailImage.module.css delete mode 100644 packages/components/src/FormatFile/InternalThumbnailImage.module.css.d.ts delete mode 100644 packages/components/src/FormatFile/InternalThumbnailImage.tsx create mode 100644 packages/components/src/FormatFile/types.ts create mode 100644 packages/components/src/FormatFile/useFormatFile.tsx create mode 100644 packages/components/src/FormatFile/useFormatFileStyles.ts rename packages/components/src/{FormatFile/InternalThumbnail.module.css => Thumbnail/Thumbnail.module.css} (72%) rename packages/components/src/{FormatFile/InternalThumbnail.module.css.d.ts => Thumbnail/Thumbnail.module.css.d.ts} (68%) rename packages/components/src/{FormatFile/InternalThumbnail.test.tsx => Thumbnail/Thumbnail.test.tsx} (93%) create mode 100644 packages/components/src/Thumbnail/Thumbnail.tsx create mode 100644 packages/components/src/Thumbnail/index.ts create mode 100644 packages/components/src/Thumbnail/types.ts create mode 100644 packages/components/src/Thumbnail/useThumbnail.tsx create mode 100644 packages/components/src/Thumbnail/useThumbnailImage.tsx diff --git a/packages/components/src/FormatFile/FormatFile.tsx b/packages/components/src/FormatFile/FormatFile.tsx index ca5c38b0d4..a0419d4af5 100644 --- a/packages/components/src/FormatFile/FormatFile.tsx +++ b/packages/components/src/FormatFile/FormatFile.tsx @@ -1,43 +1,13 @@ -import React from "react"; -import classnames from "classnames"; -import getHumanReadableFileSize from "filesize"; +import React, { useState } from "react"; import styles from "./FormatFile.module.css"; -import { FormatFileDeleteButton } from "./FormatFileDeleteButton"; -import { InternalThumbnail } from "./InternalThumbnail"; -import { FileUpload } from "../InputFile"; +import { FormatFileProps } from "./types"; +import { useFormatFileStyles } from "./useFormatFileStyles"; +import { useFormatFile } from "./useFormatFile"; +import { Thumbnail } from "../Thumbnail"; import { Text } from "../Text"; import { ProgressBar } from "../ProgressBar"; - -interface FormatFileProps { - /** - * File upload details object. (See FileUpload type.) - */ - readonly file: FileUpload; - - /** - * To display as either a file row or thumbnail - * - * @default "expanded" - */ - readonly display?: "expanded" | "compact"; - - /** - * The base dimensions of the thumbnail - * - * @default "base" - */ - readonly displaySize?: "base" | "large"; - - /** - * Function to execute when format file is clicked - */ - onClick?(event: React.MouseEvent): void; - - /** - * onDelete callback - this function will be called when the delete action is triggered - */ - onDelete?(): void; -} +import { Button } from "../Button"; +import { ConfirmationModal } from "../ConfirmationModal"; export function FormatFile({ file, @@ -46,25 +16,19 @@ export function FormatFile({ onDelete, onClick, }: FormatFileProps) { - const isComplete = file.progress >= 1; - const fileSize = getHumanReadableFileSize(file.size); - const wrapperClassNames = classnames(styles[display], styles.formatFile, { - [styles[displaySize]]: display === "compact", + const { isComplete, DetailsContainer, fileSize } = useFormatFile({ + onClick, + file, }); - const DetailsContainer = isComplete && onClick ? "button" : "div"; - - const detailsClassNames = classnames(styles.wrapper, { - [styles[displaySize]]: display === "compact", - [styles.hoverable]: isHoverable({ display, isComplete, onClick, onDelete }), - [styles.clickable]: onClick, - [styles.deleteable]: display === "compact", - }); - - const thumbnailContainerClassNames = classnames( - styles.thumbnail, - styles[displaySize], - ); + const { wrapperClassNames, detailsClassNames, thumbnailContainerClassNames } = + useFormatFileStyles({ + display, + displaySize, + onClick: !!onClick, + onDelete: !!onDelete, + isComplete, + }); return (
@@ -76,56 +40,97 @@ export function FormatFile({ aria-busy={!isComplete} >
- - - {!isComplete && ( -
- -
- )} +
- - {display === "expanded" && ( -
- {file.name} - {fileSize} -
- )} + - {isComplete && onDelete && ( -
- -
- )} +
); } -function isHoverable({ +FormatFile.Progress = function Progress({ + file, + isComplete, +}: Pick & { readonly isComplete: boolean }) { + if (isComplete) { + return null; + } + + return ( +
+ +
+ ); +}; + +FormatFile.Expanded = function Expanded({ display, + file, + fileSize, +}: Pick & { readonly fileSize: string }) { + if (display !== "expanded") { + return null; + } + + return ( +
+ {file.name} + {fileSize} +
+ ); +}; + +FormatFile.DeleteButton = function DeleteButton({ isComplete, - onClick, + displaySize, onDelete, -}: Pick & { - isComplete: boolean; -}): boolean { - if (display === "compact") { - return Boolean(isComplete && (onClick || onDelete)); - } else if (display === "expanded") { - return Boolean(isComplete && onClick); +}: Pick & { + readonly isComplete: boolean; +}) { + if (!isComplete || !onDelete) { + return null; } + const buttonSize = displaySize === "base" ? "small" : "base"; + const [deleteConfirmationOpen, setDeleteConfirmationOpen] = useState(false); - return false; -} + return ( +
+
+ ); +}; diff --git a/packages/components/src/FormatFile/FormatFileDeleteButton.tsx b/packages/components/src/FormatFile/FormatFileDeleteButton.tsx deleted file mode 100644 index ed18934eb7..0000000000 --- a/packages/components/src/FormatFile/FormatFileDeleteButton.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { useState } from "react"; -import styles from "./FormatFile.module.css"; -import { Button } from "../Button"; -import { ConfirmationModal } from "../ConfirmationModal"; - -interface DeleteButtonProps { - readonly size?: "base" | "large"; - readonly onDelete?: () => void; -} - -export function FormatFileDeleteButton({ size, onDelete }: DeleteButtonProps) { - const buttonSize = size === "base" ? "small" : "base"; - const [deleteConfirmationOpen, setDeleteConfirmationOpen] = useState(false); - - return ( -
-
- ); -} diff --git a/packages/components/src/FormatFile/InternalThumbnail.tsx b/packages/components/src/FormatFile/InternalThumbnail.tsx deleted file mode 100644 index 7e6b545f7a..0000000000 --- a/packages/components/src/FormatFile/InternalThumbnail.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from "react"; -import classNames from "classnames"; -import styles from "./InternalThumbnail.module.css"; -import { InternalThumbnailImage } from "./InternalThumbnailImage"; -import { Icon, IconNames } from "../Icon"; -import { FileUpload } from "../InputFile"; -import { Typography } from "../Typography"; -import { isSafari } from "../utils/getClientBrowser"; - -interface InternalThumbnailProps { - readonly compact?: boolean; - readonly size: "base" | "large"; - readonly file: FileUpload; -} - -export function InternalThumbnail({ - compact = false, - size, - file, -}: InternalThumbnailProps) { - const { name, type } = file; - const iconName = getIconNameFromType(type); - const hasName = Boolean(name) && compact; - - if (type.startsWith("image/") && isSupportedImageType(file)) { - return ; - } - - return ( -
- - - {hasName && ( -
- - {name} - -
- )} -
- ); -} - -function getIconNameFromType(mimeType: string): IconNames { - if (mimeType.startsWith("image/")) return "image"; - if (mimeType.startsWith("video/")) return "video"; - - switch (mimeType) { - case "application/pdf": - return "pdf"; - case "application/vnd.ms-excel": - return "excel"; - default: - return "file"; - } -} - -function isSupportedImageType(file: FileUpload) { - const userAgent = - typeof document === "undefined" ? "" : window.navigator.userAgent; - const nonHeicImage = !file.type.startsWith("image/heic"); - const nonSVGImage = !file.type.startsWith("image/svg"); - - return (nonHeicImage || isSafari(userAgent)) && nonSVGImage; -} diff --git a/packages/components/src/FormatFile/InternalThumbnailImage.module.css b/packages/components/src/FormatFile/InternalThumbnailImage.module.css deleted file mode 100644 index 0deb89132f..0000000000 --- a/packages/components/src/FormatFile/InternalThumbnailImage.module.css +++ /dev/null @@ -1,20 +0,0 @@ -.image { - width: 100%; - height: 100%; - object-fit: cover; - animation: fadeIn 200ms ease; -} - -.hidden { - display: none; -} - -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} diff --git a/packages/components/src/FormatFile/InternalThumbnailImage.module.css.d.ts b/packages/components/src/FormatFile/InternalThumbnailImage.module.css.d.ts deleted file mode 100644 index 6ef5800492..0000000000 --- a/packages/components/src/FormatFile/InternalThumbnailImage.module.css.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare const styles: { - readonly "image": string; - readonly "fadeIn": string; - readonly "hidden": string; -}; -export = styles; - diff --git a/packages/components/src/FormatFile/InternalThumbnailImage.tsx b/packages/components/src/FormatFile/InternalThumbnailImage.tsx deleted file mode 100644 index 2d6139ff53..0000000000 --- a/packages/components/src/FormatFile/InternalThumbnailImage.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useEffect, useState } from "react"; -import classNames from "classnames"; -import styles from "./InternalThumbnailImage.module.css"; -import { FileUpload } from "../InputFile"; -import { Glimmer } from "../Glimmer"; - -interface InternalThumbnailImageProps { - readonly file: FileUpload; -} - -export function InternalThumbnailImage({ file }: InternalThumbnailImageProps) { - const { name, src } = file; - const [imageSource, setImageSource] = useState(); - const [imageLoaded, setImageLoaded] = useState(false); - - useEffect(() => { - src().then(url => setImageSource(url)); - }, []); - - return ( - <> - {!imageLoaded && } - - {name} - - ); - - function handleImageLoad() { - setImageLoaded(true); - } -} diff --git a/packages/components/src/FormatFile/types.ts b/packages/components/src/FormatFile/types.ts new file mode 100644 index 0000000000..4fa11b92b0 --- /dev/null +++ b/packages/components/src/FormatFile/types.ts @@ -0,0 +1,39 @@ +import { FileUpload } from "../InputFile"; + +export interface FormatFileProps { + /** + * File upload details object. (See FileUpload type.) + */ + readonly file: FileUpload; + + /** + * To display as either a file row or thumbnail + * + * @default "expanded" + */ + readonly display?: "expanded" | "compact"; + + /** + * The base dimensions of the thumbnail + * + * @default "base" + */ + readonly displaySize?: "base" | "large"; + + /** + * Function to execute when format file is clicked + */ + onClick?(event: React.MouseEvent): void; + + /** + * onDelete callback - this function will be called when the delete action is triggered + */ + onDelete?(): void; +} + +export type UseFormatFileStylesProps = Pick< + FormatFileProps, + "display" | "displaySize" +> & { onClick: boolean; isComplete: boolean; onDelete: boolean }; + +export type UseFormatFileProps = Pick; diff --git a/packages/components/src/FormatFile/useFormatFile.tsx b/packages/components/src/FormatFile/useFormatFile.tsx new file mode 100644 index 0000000000..2732c7b26a --- /dev/null +++ b/packages/components/src/FormatFile/useFormatFile.tsx @@ -0,0 +1,12 @@ +import getHumanReadableFileSize from "filesize"; +import { UseFormatFileProps } from "./types"; + +export const useFormatFile = ({ onClick, file }: UseFormatFileProps) => { + const isComplete = file.progress >= 1; + const fileSize = getHumanReadableFileSize(file.size); + + const DetailsContainer: "button" | "div" = + isComplete && onClick ? "button" : "div"; + + return { fileSize, DetailsContainer, isComplete }; +}; diff --git a/packages/components/src/FormatFile/useFormatFileStyles.ts b/packages/components/src/FormatFile/useFormatFileStyles.ts new file mode 100644 index 0000000000..7dcea201a8 --- /dev/null +++ b/packages/components/src/FormatFile/useFormatFileStyles.ts @@ -0,0 +1,39 @@ +import classnames from "classnames"; +import styles from "./FormatFile.module.css"; +import { UseFormatFileStylesProps } from "./types"; + +export const useFormatFileStyles = ({ + displaySize = "base", + onClick, + display = "expanded", + isComplete, + onDelete, +}: UseFormatFileStylesProps) => { + const wrapperClassNames = classnames(styles[display], styles.formatFile, { + [styles[displaySize]]: display === "compact", + }); + + const detailsClassNames = classnames(styles.wrapper, { + [styles[displaySize]]: display === "compact", + [styles.hoverable]: isHoverable(), + [styles.clickable]: onClick, + [styles.deleteable]: display === "compact", + }); + + const thumbnailContainerClassNames = classnames( + styles.thumbnail, + styles[displaySize], + ); + + function isHoverable(): boolean { + if (display === "compact") { + return Boolean(isComplete && (onClick || onDelete)); + } else if (display === "expanded") { + return Boolean(isComplete && onClick); + } + + return false; + } + + return { wrapperClassNames, detailsClassNames, thumbnailContainerClassNames }; +}; diff --git a/packages/components/src/FormatFile/InternalThumbnail.module.css b/packages/components/src/Thumbnail/Thumbnail.module.css similarity index 72% rename from packages/components/src/FormatFile/InternalThumbnail.module.css rename to packages/components/src/Thumbnail/Thumbnail.module.css index 8314109a74..ed5badf33e 100644 --- a/packages/components/src/FormatFile/InternalThumbnail.module.css +++ b/packages/components/src/Thumbnail/Thumbnail.module.css @@ -1,3 +1,24 @@ +.image { + width: 100%; + height: 100%; + object-fit: cover; + animation: fadeIn 200ms ease; +} + +.hidden { + display: none; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + .content { display: grid; grid-template-columns: auto; diff --git a/packages/components/src/FormatFile/InternalThumbnail.module.css.d.ts b/packages/components/src/Thumbnail/Thumbnail.module.css.d.ts similarity index 68% rename from packages/components/src/FormatFile/InternalThumbnail.module.css.d.ts rename to packages/components/src/Thumbnail/Thumbnail.module.css.d.ts index 6981807ce5..4bac23d51e 100644 --- a/packages/components/src/FormatFile/InternalThumbnail.module.css.d.ts +++ b/packages/components/src/Thumbnail/Thumbnail.module.css.d.ts @@ -1,4 +1,7 @@ declare const styles: { + readonly "image": string; + readonly "fadeIn": string; + readonly "hidden": string; readonly "content": string; readonly "hasName": string; readonly "large": string; diff --git a/packages/components/src/FormatFile/InternalThumbnail.test.tsx b/packages/components/src/Thumbnail/Thumbnail.test.tsx similarity index 93% rename from packages/components/src/FormatFile/InternalThumbnail.test.tsx rename to packages/components/src/Thumbnail/Thumbnail.test.tsx index 9f03b8046a..88e5c04e1e 100644 --- a/packages/components/src/FormatFile/InternalThumbnail.test.tsx +++ b/packages/components/src/Thumbnail/Thumbnail.test.tsx @@ -1,7 +1,7 @@ import { render } from "@testing-library/react"; import React from "react"; import { FileUpload } from "@jobber/components/InputFile"; -import { InternalThumbnail } from "@jobber/components/FormatFile/InternalThumbnail"; +import { Thumbnail } from "@jobber/components/Thumbnail"; import * as browserUtilities from "../utils/getClientBrowser"; afterEach(() => { @@ -9,12 +9,10 @@ afterEach(() => { }); function renderInternalThumbnail(file: FileUpload) { - return render( - , - ); + return render(); } -describe("InternalThumbnail", () => { +describe("Thumbnail", () => { it("renders the appropriate thumbnail for an image", async () => { const file: FileUpload = { key: "abc", diff --git a/packages/components/src/Thumbnail/Thumbnail.tsx b/packages/components/src/Thumbnail/Thumbnail.tsx new file mode 100644 index 0000000000..e4ced557cd --- /dev/null +++ b/packages/components/src/Thumbnail/Thumbnail.tsx @@ -0,0 +1,61 @@ +import React from "react"; +import classNames from "classnames"; +import styles from "./Thumbnail.module.css"; +import { useThumbnailImage } from "./useThumbnailImage"; +import { ThumbnailProps } from "./types"; +import { useThumbnail } from "./useThumbnail"; +import { Typography } from "../Typography"; +import { Icon } from "../Icon"; +import { Glimmer } from "../Glimmer"; + +export function Thumbnail({ file, compact, size }: ThumbnailProps) { + const { isSupportedImageType, hasName, iconName } = useThumbnail({ + file, + compact, + }); + + return file.type.startsWith("image/") && isSupportedImageType() ? ( + + ) : ( +
+ + + {hasName && ( +
+ + {file.name} + +
+ )} +
+ ); +} + +Thumbnail.Image = function ThumbnailImage({ + file, +}: Pick) { + const { imageLoaded, imageSource, handleImageLoad } = useThumbnailImage({ + file, + }); + + return ( + <> + {!imageLoaded && } + + {file.name} + + ); +}; diff --git a/packages/components/src/Thumbnail/index.ts b/packages/components/src/Thumbnail/index.ts new file mode 100644 index 0000000000..d376b7afd4 --- /dev/null +++ b/packages/components/src/Thumbnail/index.ts @@ -0,0 +1 @@ +export * from "./Thumbnail"; diff --git a/packages/components/src/Thumbnail/types.ts b/packages/components/src/Thumbnail/types.ts new file mode 100644 index 0000000000..527b5fe5df --- /dev/null +++ b/packages/components/src/Thumbnail/types.ts @@ -0,0 +1,7 @@ +import { FileUpload } from "../InputFile"; + +export interface ThumbnailProps { + file: FileUpload; + size: "base" | "large"; + readonly compact: boolean; +} diff --git a/packages/components/src/Thumbnail/useThumbnail.tsx b/packages/components/src/Thumbnail/useThumbnail.tsx new file mode 100644 index 0000000000..6b206eda9f --- /dev/null +++ b/packages/components/src/Thumbnail/useThumbnail.tsx @@ -0,0 +1,36 @@ +import { IconNames } from "@jobber/design"; +import { ThumbnailProps } from "./types"; +import { isSafari } from "../utils/getClientBrowser"; + +export const useThumbnail = ({ + file, + compact, +}: Pick & { compact: boolean }) => { + const iconName = getIconNameFromType(file.type); + const hasName = Boolean(file.name) && compact; + + function getIconNameFromType(mimeType: string): IconNames { + if (mimeType.startsWith("image/")) return "image"; + if (mimeType.startsWith("video/")) return "video"; + + switch (mimeType) { + case "application/pdf": + return "pdf"; + case "application/vnd.ms-excel": + return "excel"; + default: + return "file"; + } + } + + function isSupportedImageType() { + const userAgent = + typeof document === "undefined" ? "" : window.navigator.userAgent; + const nonHeicImage = !file.type.startsWith("image/heic"); + const nonSVGImage = !file.type.startsWith("image/svg"); + + return (nonHeicImage || isSafari(userAgent)) && nonSVGImage; + } + + return { isSupportedImageType, iconName, hasName }; +}; diff --git a/packages/components/src/Thumbnail/useThumbnailImage.tsx b/packages/components/src/Thumbnail/useThumbnailImage.tsx new file mode 100644 index 0000000000..a7f6234152 --- /dev/null +++ b/packages/components/src/Thumbnail/useThumbnailImage.tsx @@ -0,0 +1,17 @@ +import { useEffect, useState } from "react"; +import { ThumbnailProps } from "./types"; + +export const useThumbnailImage = ({ file }: Pick) => { + const [imageSource, setImageSource] = useState(); + const [imageLoaded, setImageLoaded] = useState(false); + + useEffect(() => { + file.src().then((url: string) => setImageSource(url)); + }, []); + + function handleImageLoad() { + setImageLoaded(true); + } + + return { handleImageLoad, imageSource, imageLoaded }; +}; From bc2aa2d544ffcbf3a0938f8240125a92c895a33e Mon Sep 17 00:00:00 2001 From: Scott Thompson <145565743+scotttjob@users.noreply.github.com> Date: Fri, 17 Jan 2025 11:57:03 -0400 Subject: [PATCH 2/5] refactorting format file to be composable --- packages/components/src/FormatFile/FormatFile.tsx | 2 +- packages/components/src/Thumbnail/index.ts | 3 +++ packages/components/src/index.ts | 1 + packages/components/src/utils/meta/meta.json | 7 ++++++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/components/src/FormatFile/FormatFile.tsx b/packages/components/src/FormatFile/FormatFile.tsx index a0419d4af5..d91a2f8af1 100644 --- a/packages/components/src/FormatFile/FormatFile.tsx +++ b/packages/components/src/FormatFile/FormatFile.tsx @@ -118,7 +118,7 @@ FormatFile.DeleteButton = function DeleteButton({ onClick={() => setDeleteConfirmationOpen(true)} variation="destructive" type="tertiary" - icon="trash" + icon="remove" ariaLabel="Delete File" size={buttonSize} /> diff --git a/packages/components/src/Thumbnail/index.ts b/packages/components/src/Thumbnail/index.ts index d376b7afd4..00618795dc 100644 --- a/packages/components/src/Thumbnail/index.ts +++ b/packages/components/src/Thumbnail/index.ts @@ -1 +1,4 @@ export * from "./Thumbnail"; +export * from "./useThumbnail"; +export * from "./useThumbnailImage"; +export { default as thumbnailStyles } from "./Thumbnail.module.css"; diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 530ed705aa..272ad9723f 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -72,6 +72,7 @@ export * from "./StatusLabel"; export * from "./Switch"; export * from "./Table"; export * from "./Tabs"; +export * from "./Thumbnail"; export * from "./Text"; export * from "./Toast"; export * from "./Tooltip"; diff --git a/packages/components/src/utils/meta/meta.json b/packages/components/src/utils/meta/meta.json index b0b6a9b0fa..be02ffeb76 100644 --- a/packages/components/src/utils/meta/meta.json +++ b/packages/components/src/utils/meta/meta.json @@ -61,6 +61,9 @@ "FormatDate", "FormatEmail", "FormatFile", + "FormatFile.DeleteButton", + "FormatFile.Expanded", + "FormatFile.Progress", "FormatRelativeDateTime", "FormatTime", "Gallery", @@ -122,8 +125,10 @@ "Table", "Tabs", "Text", + "Thumbnail", + "Thumbnail.Image", "Toast", "Tooltip", "Typography" ] -} +} \ No newline at end of file From a6746fc7c400bc9025222dab59cca170041a1e96 Mon Sep 17 00:00:00 2001 From: Scott Thompson <145565743+scotttjob@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:07:06 -0400 Subject: [PATCH 3/5] adding slots --- .../components/src/FormatFile/FormatFile.tsx | 20 ++++++++++--------- packages/components/src/FormatFile/types.ts | 10 ++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/components/src/FormatFile/FormatFile.tsx b/packages/components/src/FormatFile/FormatFile.tsx index d91a2f8af1..24b7df8f7b 100644 --- a/packages/components/src/FormatFile/FormatFile.tsx +++ b/packages/components/src/FormatFile/FormatFile.tsx @@ -15,6 +15,7 @@ export function FormatFile({ displaySize = "base", onDelete, onClick, + slots, }: FormatFileProps) { const { isComplete, DetailsContainer, fileSize } = useFormatFile({ onClick, @@ -30,6 +31,11 @@ export function FormatFile({ isComplete, }); + const ThumbnailSlot = slots?.thumbnail || Thumbnail; + const ExpandedSlot = slots?.expanded || FormatFile.Expanded; + const DeleteButtonSlot = slots?.deleteButton || FormatFile.DeleteButton; + const ProgressSlot = slots?.progress || FormatFile.Progress; + return (
- - +
- +
- setDeleteConfirmationOpen(true)} variation="destructive" type="tertiary" - icon="remove" + icon="trash" ariaLabel="Delete File" size={buttonSize} /> diff --git a/packages/components/src/FormatFile/types.ts b/packages/components/src/FormatFile/types.ts index 4fa11b92b0..2fc094a73a 100644 --- a/packages/components/src/FormatFile/types.ts +++ b/packages/components/src/FormatFile/types.ts @@ -29,6 +29,16 @@ export interface FormatFileProps { * onDelete callback - this function will be called when the delete action is triggered */ onDelete?(): void; + + /** + * Available Component Overrides + */ + slots: { + thumbnail?: React.FC; + progress?: React.FC; + expanded?: React.FC; + deleteButton?: React.FC; + }; } export type UseFormatFileStylesProps = Pick< From d2357bffc99f5460fa850ced2b714f83fe013a1d Mon Sep 17 00:00:00 2001 From: Scott Thompson <145565743+scotttjob@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:10:11 -0400 Subject: [PATCH 4/5] adding slot example --- docs/components/FormatFile/Web.stories.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/components/FormatFile/Web.stories.tsx b/docs/components/FormatFile/Web.stories.tsx index ab93e13a85..8bdd074bab 100644 --- a/docs/components/FormatFile/Web.stories.tsx +++ b/docs/components/FormatFile/Web.stories.tsx @@ -57,3 +57,23 @@ ExpandedWithDelete.args = { onDelete: () => console.log("Deleted"), onClick: () => alert("Clicked"), }; + +export const Overridden = BasicTemplate.bind({}); +Overridden.args = { + file: { + key: "abc", + name: "image_of_something.png", + type: "image/png", + size: 213402324, + progress: 1, + src: () => Promise.resolve("https://picsum.photos/250"), + }, + slots: { + deleteButton: () => ( + + ), + }, + display: "expanded", + onDelete: () => console.log("Deleted"), + onClick: () => alert("Clicked"), +}; From 2ae7112b3d9afcca4fec55ca690f32ed164302b6 Mon Sep 17 00:00:00 2001 From: Scott Thompson <145565743+scotttjob@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:21:10 -0400 Subject: [PATCH 5/5] fixing type --- packages/components/src/FormatFile/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/FormatFile/types.ts b/packages/components/src/FormatFile/types.ts index 2fc094a73a..13b722bff5 100644 --- a/packages/components/src/FormatFile/types.ts +++ b/packages/components/src/FormatFile/types.ts @@ -33,7 +33,7 @@ export interface FormatFileProps { /** * Available Component Overrides */ - slots: { + slots?: { thumbnail?: React.FC; progress?: React.FC; expanded?: React.FC;