@windstream/react-shared-components 0.1.94 → 0.1.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +635 -635
- package/dist/contentful/index.esm.js +3 -3
- package/dist/contentful/index.esm.js.map +1 -1
- package/dist/contentful/index.js +3 -3
- package/dist/contentful/index.js.map +1 -1
- package/dist/core.d.ts +4 -4
- package/dist/index.esm.js +5 -13
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +5 -13
- package/dist/index.js.map +1 -1
- package/dist/next/index.esm.js +2 -2
- package/dist/next/index.esm.js.map +1 -1
- package/dist/next/index.js +2 -2
- package/dist/next/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils/index.esm.js +1 -1
- package/dist/utils/index.esm.js.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +191 -191
- package/src/components/accordion/Accordion.stories.tsx +230 -230
- package/src/components/accordion/index.test.tsx +270 -270
- package/src/components/accordion/index.tsx +70 -70
- package/src/components/accordion/types.ts +12 -12
- package/src/components/alert-card/AlertCard.stories.tsx +171 -171
- package/src/components/alert-card/index.test.tsx +152 -152
- package/src/components/alert-card/index.tsx +41 -41
- package/src/components/alert-card/types.ts +13 -13
- package/src/components/animation-wrapper/index.test.tsx +424 -424
- package/src/components/animation-wrapper/index.tsx +129 -129
- package/src/components/animation-wrapper/types.ts +11 -11
- package/src/components/brand-button/BrandButton.stories.tsx +223 -223
- package/src/components/brand-button/helpers.ts +35 -35
- package/src/components/brand-button/index.test.tsx +292 -292
- package/src/components/brand-button/index.tsx +120 -120
- package/src/components/brand-button/types.ts +38 -38
- package/src/components/button/Button.stories.tsx +108 -108
- package/src/components/button/index.test.tsx +91 -91
- package/src/components/button/index.tsx +27 -27
- package/src/components/button/types.ts +14 -14
- package/src/components/call-button/CallButton.stories.tsx +324 -324
- package/src/components/call-button/index.test.tsx +260 -260
- package/src/components/call-button/index.tsx +106 -106
- package/src/components/call-button/types.ts +16 -16
- package/src/components/checkbox/Checkbox.stories.tsx +247 -247
- package/src/components/checkbox/index.test.tsx +252 -252
- package/src/components/checkbox/index.tsx +197 -197
- package/src/components/checkbox/types.ts +27 -27
- package/src/components/checklist/Checklist.stories.tsx +150 -150
- package/src/components/checklist/index.test.tsx +231 -231
- package/src/components/checklist/index.tsx +96 -96
- package/src/components/checklist/types.ts +23 -23
- package/src/components/collapse/Collapse.stories.tsx +255 -255
- package/src/components/collapse/index.test.tsx +277 -277
- package/src/components/collapse/index.tsx +47 -47
- package/src/components/collapse/types.ts +6 -6
- package/src/components/divider/Divider.stories.tsx +205 -205
- package/src/components/divider/index.test.tsx +53 -53
- package/src/components/divider/index.tsx +22 -22
- package/src/components/divider/type.ts +3 -3
- package/src/components/image/Image.stories.tsx +113 -113
- package/src/components/image/index.test.tsx +174 -174
- package/src/components/image/index.tsx +25 -25
- package/src/components/image/types.ts +40 -40
- package/src/components/input/Input.stories.tsx +325 -325
- package/src/components/input/index.test.tsx +348 -348
- package/src/components/input/index.tsx +177 -177
- package/src/components/input/types.ts +37 -37
- package/src/components/link/Link.stories.tsx +163 -163
- package/src/components/link/index.test.tsx +199 -199
- package/src/components/link/index.tsx +116 -116
- package/src/components/link/types.ts +25 -25
- package/src/components/list/List.stories.tsx +272 -272
- package/src/components/list/index.test.tsx +166 -166
- package/src/components/list/index.tsx +88 -88
- package/src/components/list/list-item/index.tsx +38 -38
- package/src/components/list/list-item/types.ts +13 -13
- package/src/components/list/types.ts +29 -29
- package/src/components/material-icon/MaterialIcon.stories.tsx +322 -322
- package/src/components/material-icon/constants.ts +99 -99
- package/src/components/material-icon/index.test.tsx +130 -130
- package/src/components/material-icon/index.tsx +47 -47
- package/src/components/material-icon/types.ts +31 -31
- package/src/components/modal/Modal.stories.tsx +171 -171
- package/src/components/modal/index.test.tsx +310 -310
- package/src/components/modal/index.tsx +164 -164
- package/src/components/modal/types.ts +24 -24
- package/src/components/next-image/index.test.tsx +406 -406
- package/src/components/next-image/index.tsx +74 -74
- package/src/components/next-image/types.ts +1 -1
- package/src/components/pagination/index.test.tsx +521 -521
- package/src/components/pagination/index.tsx +91 -91
- package/src/components/pagination/types.ts +6 -6
- package/src/components/radio-button/RadioButton.stories.tsx +307 -307
- package/src/components/radio-button/index.test.tsx +151 -151
- package/src/components/radio-button/index.tsx +75 -75
- package/src/components/radio-button/types.ts +21 -21
- package/src/components/see-more/SeeMore.stories.tsx +181 -181
- package/src/components/see-more/index.test.tsx +96 -96
- package/src/components/see-more/index.tsx +44 -44
- package/src/components/see-more/types.ts +4 -4
- package/src/components/select/Select.stories.tsx +411 -411
- package/src/components/select/index.test.tsx +256 -256
- package/src/components/select/index.tsx +155 -155
- package/src/components/select/types.ts +36 -36
- package/src/components/select-plan-button/SelectPlanButton.stories.tsx +184 -184
- package/src/components/select-plan-button/index.test.tsx +173 -173
- package/src/components/select-plan-button/index.tsx +63 -63
- package/src/components/select-plan-button/types.ts +17 -17
- package/src/components/skeleton/Skeleton.stories.tsx +179 -179
- package/src/components/skeleton/index.test.tsx +74 -74
- package/src/components/skeleton/index.tsx +61 -61
- package/src/components/skeleton/types.ts +4 -4
- package/src/components/spinner/Spinner.stories.tsx +335 -335
- package/src/components/spinner/index.test.tsx +76 -76
- package/src/components/spinner/index.tsx +44 -44
- package/src/components/spinner/types.ts +5 -5
- package/src/components/text/Text.stories.tsx +321 -321
- package/src/components/text/index.test.tsx +65 -65
- package/src/components/text/index.tsx +25 -25
- package/src/components/text/types.ts +45 -45
- package/src/components/tooltip/Tooltip.stories.tsx +219 -219
- package/src/components/tooltip/index.test.tsx +50 -50
- package/src/components/tooltip/index.tsx +74 -74
- package/src/components/tooltip/types.ts +7 -7
- package/src/components/view-cart-button/ViewCartButton.stories.tsx +252 -252
- package/src/components/view-cart-button/index.test.tsx +57 -57
- package/src/components/view-cart-button/index.tsx +42 -42
- package/src/components/view-cart-button/types.ts +5 -5
- package/src/contentful/blocks/accordion/Accordion.stories.mocks.tsx +128 -128
- package/src/contentful/blocks/accordion/Accordion.stories.tsx +98 -98
- package/src/contentful/blocks/accordion/index.test.tsx +218 -218
- package/src/contentful/blocks/accordion/index.tsx +114 -114
- package/src/contentful/blocks/accordion/types.ts +34 -34
- package/src/contentful/blocks/address-input-banner/index.test.tsx +132 -132
- package/src/contentful/blocks/address-input-banner/index.tsx +52 -52
- package/src/contentful/blocks/address-input-banner/types.ts +14 -14
- package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +287 -287
- package/src/contentful/blocks/anchored-bottom-banner/index.tsx +181 -181
- package/src/contentful/blocks/anchored-bottom-banner/types.ts +13 -13
- package/src/contentful/blocks/blogs-grid/BlogGrid.stories.mocks.tsx +144 -144
- package/src/contentful/blocks/blogs-grid/BlogGrid.stories.tsx +157 -157
- package/src/contentful/blocks/blogs-grid/index.test.tsx +355 -355
- package/src/contentful/blocks/blogs-grid/index.tsx +134 -134
- package/src/contentful/blocks/blogs-grid/types.ts +26 -26
- package/src/contentful/blocks/blogs-grid-base/index.test.tsx +274 -274
- package/src/contentful/blocks/blogs-grid-base/index.tsx +119 -119
- package/src/contentful/blocks/blogs-grid-base/types.ts +36 -36
- package/src/contentful/blocks/breadcrumbs/BreadcrumbNavigation.stories.tsx +147 -147
- package/src/contentful/blocks/breadcrumbs/index.test.tsx +281 -281
- package/src/contentful/blocks/breadcrumbs/index.tsx +95 -95
- package/src/contentful/blocks/breadcrumbs/types.ts +8 -8
- package/src/contentful/blocks/button/Button.stories.tsx +40 -40
- package/src/contentful/blocks/button/index.test.tsx +339 -339
- package/src/contentful/blocks/button/index.tsx +131 -131
- package/src/contentful/blocks/button/types.ts +39 -39
- package/src/contentful/blocks/callout/Callout.stories.tsx +23 -23
- package/src/contentful/blocks/callout/index.test.tsx +539 -539
- package/src/contentful/blocks/callout/index.tsx +277 -277
- package/src/contentful/blocks/callout/types.ts +78 -78
- package/src/contentful/blocks/cards/Cards.stories.tsx +23 -23
- package/src/contentful/blocks/cards/blog-card/index.test.tsx +218 -218
- package/src/contentful/blocks/cards/blog-card/index.tsx +129 -129
- package/src/contentful/blocks/cards/blog-card/types.ts +34 -34
- package/src/contentful/blocks/cards/floating-image-card/index.test.tsx +201 -201
- package/src/contentful/blocks/cards/floating-image-card/index.tsx +119 -119
- package/src/contentful/blocks/cards/floating-image-card/types.ts +30 -30
- package/src/contentful/blocks/cards/full-image-card/index.test.tsx +216 -216
- package/src/contentful/blocks/cards/full-image-card/index.tsx +130 -130
- package/src/contentful/blocks/cards/full-image-card/types.ts +29 -29
- package/src/contentful/blocks/cards/index.test.tsx +39 -39
- package/src/contentful/blocks/cards/index.tsx +13 -13
- package/src/contentful/blocks/cards/product-card/index.test.tsx +263 -263
- package/src/contentful/blocks/cards/product-card/index.tsx +251 -251
- package/src/contentful/blocks/cards/product-card/types.ts +28 -28
- package/src/contentful/blocks/cards/simple-card/index.test.tsx +364 -364
- package/src/contentful/blocks/cards/simple-card/index.tsx +325 -325
- package/src/contentful/blocks/cards/simple-card/types.ts +71 -71
- package/src/contentful/blocks/cards/testimonial-card/index.test.tsx +180 -180
- package/src/contentful/blocks/cards/testimonial-card/index.tsx +90 -90
- package/src/contentful/blocks/cards/testimonial-card/types.tsx +12 -12
- package/src/contentful/blocks/cards/types.ts +1 -1
- package/src/contentful/blocks/carousel/Carousel.stories.tsx +23 -23
- package/src/contentful/blocks/carousel/helper.test.tsx +539 -539
- package/src/contentful/blocks/carousel/helper.tsx +494 -494
- package/src/contentful/blocks/carousel/index.test.tsx +308 -308
- package/src/contentful/blocks/carousel/index.tsx +87 -87
- package/src/contentful/blocks/carousel/types.test.ts +16 -16
- package/src/contentful/blocks/carousel/types.ts +145 -145
- package/src/contentful/blocks/cart-retention-banner/index.test.tsx +409 -409
- package/src/contentful/blocks/cart-retention-banner/index.tsx +109 -109
- package/src/contentful/blocks/cart-retention-banner/types.ts +11 -11
- package/src/contentful/blocks/comparison-table/index.test.tsx +114 -114
- package/src/contentful/blocks/comparison-table/index.tsx +29 -29
- package/src/contentful/blocks/comparison-table/types.ts +6 -6
- package/src/contentful/blocks/cookiebanner/index.test.tsx +277 -277
- package/src/contentful/blocks/cookiebanner/index.tsx +146 -146
- package/src/contentful/blocks/cookiebanner/type.ts +7 -7
- package/src/contentful/blocks/cta-callout/CtaCallout.stories.tsx +46 -46
- package/src/contentful/blocks/cta-callout/index.test.tsx +244 -244
- package/src/contentful/blocks/cta-callout/index.tsx +73 -73
- package/src/contentful/blocks/cta-callout/types.ts +26 -26
- package/src/contentful/blocks/dynamic-tabs/index.test.tsx +240 -240
- package/src/contentful/blocks/dynamic-tabs/index.tsx +204 -204
- package/src/contentful/blocks/dynamic-tabs/types.ts +21 -21
- package/src/contentful/blocks/email-input-block/index.test.tsx +213 -213
- package/src/contentful/blocks/email-input-block/index.tsx +121 -121
- package/src/contentful/blocks/email-input-block/types.ts +16 -16
- package/src/contentful/blocks/find-kinetic/FindKinetic.stories.tsx +23 -23
- package/src/contentful/blocks/find-kinetic/index.test.tsx +269 -269
- package/src/contentful/blocks/find-kinetic/index.tsx +138 -138
- package/src/contentful/blocks/find-kinetic/types.ts +20 -20
- package/src/contentful/blocks/floating-banner/FloatingBanner.stories.tsx +34 -34
- package/src/contentful/blocks/floating-banner/index.test.tsx +246 -246
- package/src/contentful/blocks/floating-banner/index.tsx +97 -97
- package/src/contentful/blocks/floating-banner/types.ts +22 -22
- package/src/contentful/blocks/footer/Footer.stories.tsx +317 -317
- package/src/contentful/blocks/footer/index.test.tsx +302 -302
- package/src/contentful/blocks/footer/index.tsx +91 -91
- package/src/contentful/blocks/footer/types.ts +13 -13
- package/src/contentful/blocks/image-promo-bar/ImagePromoBar.stories.tsx +23 -23
- package/src/contentful/blocks/image-promo-bar/helper.test.tsx +61 -61
- package/src/contentful/blocks/image-promo-bar/helper.tsx +28 -28
- package/src/contentful/blocks/image-promo-bar/index.test.tsx +467 -467
- package/src/contentful/blocks/image-promo-bar/index.tsx +8 -6
- package/src/contentful/blocks/image-promo-bar/types.ts +44 -44
- package/src/contentful/blocks/image-promo-bar/vimeo-embed.test.tsx +142 -142
- package/src/contentful/blocks/image-promo-bar/vimeo-embed.tsx +93 -93
- package/src/contentful/blocks/image-promo-bar/youtube-embed.test.tsx +104 -104
- package/src/contentful/blocks/image-promo-bar/youtube-embed.tsx +46 -46
- package/src/contentful/blocks/modal/constants.ts +53 -53
- package/src/contentful/blocks/modal/index.test.tsx +209 -209
- package/src/contentful/blocks/modal/index.tsx +108 -108
- package/src/contentful/blocks/modal/types.ts +12 -12
- package/src/contentful/blocks/navigation/Navigation.stories.mocks.tsx +78 -78
- package/src/contentful/blocks/navigation/Navigation.stories.tsx +138 -138
- package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +208 -208
- package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.tsx +141 -141
- package/src/contentful/blocks/navigation/index.test.tsx +924 -924
- package/src/contentful/blocks/navigation/index.tsx +569 -569
- package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +131 -131
- package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.tsx +82 -82
- package/src/contentful/blocks/navigation/types.ts +71 -71
- package/src/contentful/blocks/primary-hero/PrimaryHero.stories.tsx +23 -23
- package/src/contentful/blocks/primary-hero/index.test.tsx +286 -286
- package/src/contentful/blocks/primary-hero/index.tsx +239 -239
- package/src/contentful/blocks/primary-hero/types.ts +37 -37
- package/src/contentful/blocks/search-block/index.test.tsx +268 -268
- package/src/contentful/blocks/search-block/index.tsx +90 -90
- package/src/contentful/blocks/search-block/types.ts +15 -15
- package/src/contentful/blocks/shape-background-wrapper/ShapeBackgroundWrapper.stories.tsx +26 -26
- package/src/contentful/blocks/shape-background-wrapper/index.test.tsx +284 -284
- package/src/contentful/blocks/shape-background-wrapper/index.tsx +124 -124
- package/src/contentful/blocks/shape-background-wrapper/types.ts +36 -36
- package/src/contentful/blocks/text/Text.stories.tsx +23 -23
- package/src/contentful/blocks/text/index.test.tsx +36 -36
- package/src/contentful/blocks/text/index.tsx +12 -12
- package/src/contentful/blocks/text/types.ts +1 -1
- package/src/contentful/index.test.ts +45 -45
- package/src/contentful/index.ts +105 -105
- package/src/global-mocks/contentful/to-document.ts +25 -25
- package/src/global-mocks/cookie.ts +48 -48
- package/src/global-mocks/cx.ts +37 -37
- package/src/global-mocks/index.ts +89 -89
- package/src/global-mocks/speed-card-bg.ts +27 -27
- package/src/global-mocks/utm.ts +49 -49
- package/src/hooks/contentful/use-contentful-rich-text.test.tsx +1758 -1758
- package/src/hooks/contentful/use-contentful-rich-text.tsx +309 -309
- package/src/hooks/contentful/use-processed-check-list.test.tsx +277 -277
- package/src/hooks/contentful/use-processed-check-list.ts +63 -63
- package/src/hooks/use-body-scroll-lock.test.ts +134 -134
- package/src/hooks/use-body-scroll-lock.ts +34 -34
- package/src/hooks/use-carousel-swipe.test.ts +393 -393
- package/src/hooks/use-carousel-swipe.ts +264 -264
- package/src/hooks/use-outside-click.test.ts +142 -142
- package/src/hooks/use-outside-click.ts +17 -17
- package/src/index.ts +107 -107
- package/src/next/index.test.ts +7 -7
- package/src/next/index.ts +5 -5
- package/src/setupTests.ts +52 -52
- package/src/stories/DocsTemplate.tsx +24 -24
- package/src/styles/globals.css +343 -343
- package/src/types/global.d.ts +9 -9
- package/src/types/micro-components.ts +99 -99
- package/src/types/utm.ts +49 -49
- package/src/utils/contentful/to-document.test.ts +85 -85
- package/src/utils/contentful/to-document.ts +24 -24
- package/src/utils/cookie.test.ts +180 -180
- package/src/utils/cookie.ts +84 -84
- package/src/utils/cx.test.ts +90 -90
- package/src/utils/cx.ts +49 -49
- package/src/utils/index.test.ts +115 -115
- package/src/utils/index.ts +41 -41
- package/src/utils/speed-card-bg.test.ts +46 -46
- package/src/utils/speed-card-bg.ts +24 -24
- package/src/utils/utm.test.ts +359 -359
- package/src/utils/utm.ts +221 -221
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
import { forwardRef } from "react";
|
|
2
|
-
import { Image } from "./index";
|
|
3
|
-
import type { ImageComponentProps } from "./types";
|
|
4
|
-
|
|
5
|
-
import { DocsPage } from "@shared/stories/DocsTemplate";
|
|
6
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
7
|
-
|
|
8
|
-
const meta: Meta<typeof Image> = {
|
|
9
|
-
title: "Components/Image",
|
|
10
|
-
component: Image,
|
|
11
|
-
tags: ["autodocs"],
|
|
12
|
-
parameters: {
|
|
13
|
-
layout: "centered",
|
|
14
|
-
docs: {
|
|
15
|
-
page: DocsPage,
|
|
16
|
-
description: {
|
|
17
|
-
component:
|
|
18
|
-
"A reusable image component that renders a native img by default. Pass the `as` prop with a custom component (e.g. next/image) to use Next.js Image or another image component.",
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
argTypes: {
|
|
23
|
-
src: {
|
|
24
|
-
control: { type: "text" },
|
|
25
|
-
description: "Image source URL",
|
|
26
|
-
},
|
|
27
|
-
alt: {
|
|
28
|
-
control: { type: "text" },
|
|
29
|
-
description:
|
|
30
|
-
"Alt text (required for accessibility; use empty string for decorative images)",
|
|
31
|
-
},
|
|
32
|
-
width: {
|
|
33
|
-
control: { type: "number" },
|
|
34
|
-
description: "Image width",
|
|
35
|
-
},
|
|
36
|
-
height: {
|
|
37
|
-
control: { type: "number" },
|
|
38
|
-
description: "Image height",
|
|
39
|
-
},
|
|
40
|
-
className: {
|
|
41
|
-
control: { type: "text" },
|
|
42
|
-
description: "Custom CSS classes",
|
|
43
|
-
},
|
|
44
|
-
loading: {
|
|
45
|
-
control: { type: "select" },
|
|
46
|
-
options: ["lazy", "eager"],
|
|
47
|
-
description: "Loading behavior",
|
|
48
|
-
},
|
|
49
|
-
as: {
|
|
50
|
-
description:
|
|
51
|
-
"Custom component to render (e.g. next/image). In a Next.js app: import Image from 'next/image' then use <Image as={Image} src={...} alt={...} width={...} height={...} />",
|
|
52
|
-
control: false,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
args: {
|
|
56
|
-
src: "https://placehold.co/400x300",
|
|
57
|
-
alt: "Placeholder image",
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export default meta;
|
|
62
|
-
type Story = StoryObj<typeof meta>;
|
|
63
|
-
|
|
64
|
-
export const Default: Story = {
|
|
65
|
-
args: {},
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const WithDimensions: Story = {
|
|
69
|
-
args: {
|
|
70
|
-
src: "https://placehold.co/300x200",
|
|
71
|
-
alt: "Image with explicit dimensions",
|
|
72
|
-
width: 300,
|
|
73
|
-
height: 200,
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export const WithClassName: Story = {
|
|
78
|
-
args: {
|
|
79
|
-
src: "https://placehold.co/400x300",
|
|
80
|
-
alt: "Styled image",
|
|
81
|
-
className: "rounded-lg object-cover shadow-md",
|
|
82
|
-
width: 400,
|
|
83
|
-
height: 300,
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
/** Mock component to demonstrate the `as` prop. In a Next.js app, pass next/image: <Image as={NextImage} ... /> */
|
|
88
|
-
const MockImageComponent = forwardRef<HTMLImageElement, ImageComponentProps>(
|
|
89
|
-
(props, ref) => <img ref={ref} {...props} />
|
|
90
|
-
);
|
|
91
|
-
MockImageComponent.displayName = "MockImageComponent";
|
|
92
|
-
|
|
93
|
-
export const WithCustomComponent: Story = {
|
|
94
|
-
render: args => (
|
|
95
|
-
<Image
|
|
96
|
-
{...args}
|
|
97
|
-
as={MockImageComponent}
|
|
98
|
-
src="https://placehold.co/350x150"
|
|
99
|
-
alt="Rendered via custom component (as prop)"
|
|
100
|
-
width={350}
|
|
101
|
-
height={150}
|
|
102
|
-
className="rounded border-2 border-dashed border-gray-300"
|
|
103
|
-
/>
|
|
104
|
-
),
|
|
105
|
-
parameters: {
|
|
106
|
-
docs: {
|
|
107
|
-
description: {
|
|
108
|
-
story:
|
|
109
|
-
"Use the `as` prop to render with a custom component. In Next.js, pass the Image component from next/image for optimized images.",
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
};
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
import { Image } from "./index";
|
|
3
|
+
import type { ImageComponentProps } from "./types";
|
|
4
|
+
|
|
5
|
+
import { DocsPage } from "@shared/stories/DocsTemplate";
|
|
6
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Image> = {
|
|
9
|
+
title: "Components/Image",
|
|
10
|
+
component: Image,
|
|
11
|
+
tags: ["autodocs"],
|
|
12
|
+
parameters: {
|
|
13
|
+
layout: "centered",
|
|
14
|
+
docs: {
|
|
15
|
+
page: DocsPage,
|
|
16
|
+
description: {
|
|
17
|
+
component:
|
|
18
|
+
"A reusable image component that renders a native img by default. Pass the `as` prop with a custom component (e.g. next/image) to use Next.js Image or another image component.",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
argTypes: {
|
|
23
|
+
src: {
|
|
24
|
+
control: { type: "text" },
|
|
25
|
+
description: "Image source URL",
|
|
26
|
+
},
|
|
27
|
+
alt: {
|
|
28
|
+
control: { type: "text" },
|
|
29
|
+
description:
|
|
30
|
+
"Alt text (required for accessibility; use empty string for decorative images)",
|
|
31
|
+
},
|
|
32
|
+
width: {
|
|
33
|
+
control: { type: "number" },
|
|
34
|
+
description: "Image width",
|
|
35
|
+
},
|
|
36
|
+
height: {
|
|
37
|
+
control: { type: "number" },
|
|
38
|
+
description: "Image height",
|
|
39
|
+
},
|
|
40
|
+
className: {
|
|
41
|
+
control: { type: "text" },
|
|
42
|
+
description: "Custom CSS classes",
|
|
43
|
+
},
|
|
44
|
+
loading: {
|
|
45
|
+
control: { type: "select" },
|
|
46
|
+
options: ["lazy", "eager"],
|
|
47
|
+
description: "Loading behavior",
|
|
48
|
+
},
|
|
49
|
+
as: {
|
|
50
|
+
description:
|
|
51
|
+
"Custom component to render (e.g. next/image). In a Next.js app: import Image from 'next/image' then use <Image as={Image} src={...} alt={...} width={...} height={...} />",
|
|
52
|
+
control: false,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
args: {
|
|
56
|
+
src: "https://placehold.co/400x300",
|
|
57
|
+
alt: "Placeholder image",
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default meta;
|
|
62
|
+
type Story = StoryObj<typeof meta>;
|
|
63
|
+
|
|
64
|
+
export const Default: Story = {
|
|
65
|
+
args: {},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const WithDimensions: Story = {
|
|
69
|
+
args: {
|
|
70
|
+
src: "https://placehold.co/300x200",
|
|
71
|
+
alt: "Image with explicit dimensions",
|
|
72
|
+
width: 300,
|
|
73
|
+
height: 200,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const WithClassName: Story = {
|
|
78
|
+
args: {
|
|
79
|
+
src: "https://placehold.co/400x300",
|
|
80
|
+
alt: "Styled image",
|
|
81
|
+
className: "rounded-lg object-cover shadow-md",
|
|
82
|
+
width: 400,
|
|
83
|
+
height: 300,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/** Mock component to demonstrate the `as` prop. In a Next.js app, pass next/image: <Image as={NextImage} ... /> */
|
|
88
|
+
const MockImageComponent = forwardRef<HTMLImageElement, ImageComponentProps>(
|
|
89
|
+
(props, ref) => <img ref={ref} {...props} />
|
|
90
|
+
);
|
|
91
|
+
MockImageComponent.displayName = "MockImageComponent";
|
|
92
|
+
|
|
93
|
+
export const WithCustomComponent: Story = {
|
|
94
|
+
render: args => (
|
|
95
|
+
<Image
|
|
96
|
+
{...args}
|
|
97
|
+
as={MockImageComponent}
|
|
98
|
+
src="https://placehold.co/350x150"
|
|
99
|
+
alt="Rendered via custom component (as prop)"
|
|
100
|
+
width={350}
|
|
101
|
+
height={150}
|
|
102
|
+
className="rounded border-2 border-dashed border-gray-300"
|
|
103
|
+
/>
|
|
104
|
+
),
|
|
105
|
+
parameters: {
|
|
106
|
+
docs: {
|
|
107
|
+
description: {
|
|
108
|
+
story:
|
|
109
|
+
"Use the `as` prop to render with a custom component. In Next.js, pass the Image component from next/image for optimized images.",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
};
|
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
import "@testing-library/jest-dom";
|
|
2
|
-
|
|
3
|
-
import React, { createRef, forwardRef } from "react";
|
|
4
|
-
import { Image } from "./index";
|
|
5
|
-
import { ImageComponentProps } from "./types";
|
|
6
|
-
|
|
7
|
-
import { render, screen } from "@testing-library/react";
|
|
8
|
-
|
|
9
|
-
// Mock cx
|
|
10
|
-
jest.mock("@shared/utils", () => ({
|
|
11
|
-
cx: (...args: any[]) => args.filter(Boolean).join(" "),
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
|
-
describe("Image", () => {
|
|
15
|
-
it("has displayName set to Image", () => {
|
|
16
|
-
expect(Image.displayName).toBe("Image");
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
describe("native img rendering", () => {
|
|
20
|
-
it("renders a native img element by default", () => {
|
|
21
|
-
render(<Image src="/test.png" alt="Test image" />);
|
|
22
|
-
const img = screen.getByAltText("Test image");
|
|
23
|
-
expect(img.tagName).toBe("IMG");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("passes src prop to img", () => {
|
|
27
|
-
render(<Image src="/photo.jpg" alt="Photo" />);
|
|
28
|
-
const img = screen.getByAltText("Photo");
|
|
29
|
-
expect(img).toHaveAttribute("src", "/photo.jpg");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("passes alt prop to img", () => {
|
|
33
|
-
render(<Image src="/img.png" alt="My image" />);
|
|
34
|
-
const img = screen.getByAltText("My image");
|
|
35
|
-
expect(img).toBeInTheDocument();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("passes className to img", () => {
|
|
39
|
-
render(<Image src="/img.png" alt="Img" className="h-auto w-full" />);
|
|
40
|
-
const img = screen.getByAltText("Img");
|
|
41
|
-
expect(img).toHaveClass("w-full");
|
|
42
|
-
expect(img).toHaveClass("h-auto");
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("uses empty string className by default", () => {
|
|
46
|
-
render(<Image src="/img.png" alt="Img" />);
|
|
47
|
-
const img = screen.getByAltText("Img");
|
|
48
|
-
expect(img).toBeInTheDocument();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("passes additional HTML img attributes", () => {
|
|
52
|
-
render(
|
|
53
|
-
<Image
|
|
54
|
-
src="/img.png"
|
|
55
|
-
alt="Img"
|
|
56
|
-
width={200}
|
|
57
|
-
height={100}
|
|
58
|
-
loading="lazy"
|
|
59
|
-
/>
|
|
60
|
-
);
|
|
61
|
-
const img = screen.getByAltText("Img");
|
|
62
|
-
expect(img).toHaveAttribute("width", "200");
|
|
63
|
-
expect(img).toHaveAttribute("height", "100");
|
|
64
|
-
expect(img).toHaveAttribute("loading", "lazy");
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe("ref forwarding", () => {
|
|
69
|
-
it("forwards ref to native img element", () => {
|
|
70
|
-
const ref = createRef<HTMLImageElement>();
|
|
71
|
-
render(<Image ref={ref} src="/img.png" alt="Ref test" />);
|
|
72
|
-
expect(ref.current).toBeInstanceOf(HTMLImageElement);
|
|
73
|
-
expect(ref.current?.tagName).toBe("IMG");
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("forwards ref to custom component", () => {
|
|
77
|
-
const ref = createRef<HTMLImageElement>();
|
|
78
|
-
const CustomImage = forwardRef<HTMLImageElement, ImageComponentProps>(
|
|
79
|
-
function CustomImage(props, fwdRef) {
|
|
80
|
-
return <img ref={fwdRef} data-testid="custom-img" {...props} />;
|
|
81
|
-
}
|
|
82
|
-
);
|
|
83
|
-
render(
|
|
84
|
-
<Image ref={ref} as={CustomImage} src="/img.png" alt="Custom ref" />
|
|
85
|
-
);
|
|
86
|
-
expect(ref.current).toBeInstanceOf(HTMLImageElement);
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe("polymorphic rendering with 'as' prop", () => {
|
|
91
|
-
it("renders custom component when 'as' is provided", () => {
|
|
92
|
-
const CustomImage = (props: any) => (
|
|
93
|
-
<div data-testid="custom-image" data-src={props.src}>
|
|
94
|
-
{props.alt}
|
|
95
|
-
</div>
|
|
96
|
-
);
|
|
97
|
-
render(<Image as={CustomImage} src="/custom.png" alt="Custom" />);
|
|
98
|
-
expect(screen.getByTestId("custom-image")).toBeInTheDocument();
|
|
99
|
-
expect(screen.getByTestId("custom-image")).toHaveAttribute(
|
|
100
|
-
"data-src",
|
|
101
|
-
"/custom.png"
|
|
102
|
-
);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("passes className to custom component", () => {
|
|
106
|
-
const CustomImage = ({ className, ...props }: any) => (
|
|
107
|
-
<img data-testid="custom-img" className={className} {...props} />
|
|
108
|
-
);
|
|
109
|
-
render(
|
|
110
|
-
<Image as={CustomImage} src="/img.png" alt="Cls" className="rounded" />
|
|
111
|
-
);
|
|
112
|
-
const img = screen.getByTestId("custom-img");
|
|
113
|
-
expect(img).toHaveClass("rounded");
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("passes all props to custom component", () => {
|
|
117
|
-
const CustomImage = (props: any) => (
|
|
118
|
-
<img
|
|
119
|
-
data-testid="custom-img"
|
|
120
|
-
data-fill={props.fill?.toString()}
|
|
121
|
-
data-priority={props.priority?.toString()}
|
|
122
|
-
src={props.src}
|
|
123
|
-
alt={props.alt}
|
|
124
|
-
/>
|
|
125
|
-
);
|
|
126
|
-
render(
|
|
127
|
-
<Image
|
|
128
|
-
as={CustomImage}
|
|
129
|
-
src="/img.png"
|
|
130
|
-
alt="Props test"
|
|
131
|
-
fill={true}
|
|
132
|
-
priority={true}
|
|
133
|
-
/>
|
|
134
|
-
);
|
|
135
|
-
const img = screen.getByTestId("custom-img");
|
|
136
|
-
expect(img).toHaveAttribute("data-fill", "true");
|
|
137
|
-
expect(img).toHaveAttribute("data-priority", "true");
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("does not render native img when as prop is provided", () => {
|
|
141
|
-
const CustomImage = (props: any) => (
|
|
142
|
-
<div data-testid="custom-wrapper">{props.alt}</div>
|
|
143
|
-
);
|
|
144
|
-
render(<Image as={CustomImage} src="/img.png" alt="No native" />);
|
|
145
|
-
expect(screen.queryByRole("img")).not.toBeInTheDocument();
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("renders forwardRef component via as prop", () => {
|
|
149
|
-
const ForwardRefImage = forwardRef<HTMLImageElement, ImageComponentProps>(
|
|
150
|
-
function ForwardRefImage(props, fwdRef) {
|
|
151
|
-
return <img ref={fwdRef} data-testid="fwdref-img" {...props} />;
|
|
152
|
-
}
|
|
153
|
-
);
|
|
154
|
-
render(
|
|
155
|
-
<Image as={ForwardRefImage} src="/img.png" alt="ForwardRef test" />
|
|
156
|
-
);
|
|
157
|
-
expect(screen.getByTestId("fwdref-img")).toBeInTheDocument();
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
describe("edge cases", () => {
|
|
162
|
-
it("renders with empty alt for decorative images", () => {
|
|
163
|
-
const { container } = render(<Image src="/decorative.png" alt="" />);
|
|
164
|
-
const img = container.querySelector("img");
|
|
165
|
-
expect(img).toHaveAttribute("alt", "");
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it("renders with no className (defaults to empty string)", () => {
|
|
169
|
-
const { container } = render(<Image src="/img.png" alt="No class" />);
|
|
170
|
-
const img = container.querySelector("img");
|
|
171
|
-
expect(img).toBeInTheDocument();
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
});
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
|
|
3
|
+
import React, { createRef, forwardRef } from "react";
|
|
4
|
+
import { Image } from "./index";
|
|
5
|
+
import { ImageComponentProps } from "./types";
|
|
6
|
+
|
|
7
|
+
import { render, screen } from "@testing-library/react";
|
|
8
|
+
|
|
9
|
+
// Mock cx
|
|
10
|
+
jest.mock("@shared/utils", () => ({
|
|
11
|
+
cx: (...args: any[]) => args.filter(Boolean).join(" "),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
describe("Image", () => {
|
|
15
|
+
it("has displayName set to Image", () => {
|
|
16
|
+
expect(Image.displayName).toBe("Image");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("native img rendering", () => {
|
|
20
|
+
it("renders a native img element by default", () => {
|
|
21
|
+
render(<Image src="/test.png" alt="Test image" />);
|
|
22
|
+
const img = screen.getByAltText("Test image");
|
|
23
|
+
expect(img.tagName).toBe("IMG");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("passes src prop to img", () => {
|
|
27
|
+
render(<Image src="/photo.jpg" alt="Photo" />);
|
|
28
|
+
const img = screen.getByAltText("Photo");
|
|
29
|
+
expect(img).toHaveAttribute("src", "/photo.jpg");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("passes alt prop to img", () => {
|
|
33
|
+
render(<Image src="/img.png" alt="My image" />);
|
|
34
|
+
const img = screen.getByAltText("My image");
|
|
35
|
+
expect(img).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("passes className to img", () => {
|
|
39
|
+
render(<Image src="/img.png" alt="Img" className="h-auto w-full" />);
|
|
40
|
+
const img = screen.getByAltText("Img");
|
|
41
|
+
expect(img).toHaveClass("w-full");
|
|
42
|
+
expect(img).toHaveClass("h-auto");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("uses empty string className by default", () => {
|
|
46
|
+
render(<Image src="/img.png" alt="Img" />);
|
|
47
|
+
const img = screen.getByAltText("Img");
|
|
48
|
+
expect(img).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("passes additional HTML img attributes", () => {
|
|
52
|
+
render(
|
|
53
|
+
<Image
|
|
54
|
+
src="/img.png"
|
|
55
|
+
alt="Img"
|
|
56
|
+
width={200}
|
|
57
|
+
height={100}
|
|
58
|
+
loading="lazy"
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
const img = screen.getByAltText("Img");
|
|
62
|
+
expect(img).toHaveAttribute("width", "200");
|
|
63
|
+
expect(img).toHaveAttribute("height", "100");
|
|
64
|
+
expect(img).toHaveAttribute("loading", "lazy");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("ref forwarding", () => {
|
|
69
|
+
it("forwards ref to native img element", () => {
|
|
70
|
+
const ref = createRef<HTMLImageElement>();
|
|
71
|
+
render(<Image ref={ref} src="/img.png" alt="Ref test" />);
|
|
72
|
+
expect(ref.current).toBeInstanceOf(HTMLImageElement);
|
|
73
|
+
expect(ref.current?.tagName).toBe("IMG");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("forwards ref to custom component", () => {
|
|
77
|
+
const ref = createRef<HTMLImageElement>();
|
|
78
|
+
const CustomImage = forwardRef<HTMLImageElement, ImageComponentProps>(
|
|
79
|
+
function CustomImage(props, fwdRef) {
|
|
80
|
+
return <img ref={fwdRef} data-testid="custom-img" {...props} />;
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
render(
|
|
84
|
+
<Image ref={ref} as={CustomImage} src="/img.png" alt="Custom ref" />
|
|
85
|
+
);
|
|
86
|
+
expect(ref.current).toBeInstanceOf(HTMLImageElement);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("polymorphic rendering with 'as' prop", () => {
|
|
91
|
+
it("renders custom component when 'as' is provided", () => {
|
|
92
|
+
const CustomImage = (props: any) => (
|
|
93
|
+
<div data-testid="custom-image" data-src={props.src}>
|
|
94
|
+
{props.alt}
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
render(<Image as={CustomImage} src="/custom.png" alt="Custom" />);
|
|
98
|
+
expect(screen.getByTestId("custom-image")).toBeInTheDocument();
|
|
99
|
+
expect(screen.getByTestId("custom-image")).toHaveAttribute(
|
|
100
|
+
"data-src",
|
|
101
|
+
"/custom.png"
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("passes className to custom component", () => {
|
|
106
|
+
const CustomImage = ({ className, ...props }: any) => (
|
|
107
|
+
<img data-testid="custom-img" className={className} {...props} />
|
|
108
|
+
);
|
|
109
|
+
render(
|
|
110
|
+
<Image as={CustomImage} src="/img.png" alt="Cls" className="rounded" />
|
|
111
|
+
);
|
|
112
|
+
const img = screen.getByTestId("custom-img");
|
|
113
|
+
expect(img).toHaveClass("rounded");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("passes all props to custom component", () => {
|
|
117
|
+
const CustomImage = (props: any) => (
|
|
118
|
+
<img
|
|
119
|
+
data-testid="custom-img"
|
|
120
|
+
data-fill={props.fill?.toString()}
|
|
121
|
+
data-priority={props.priority?.toString()}
|
|
122
|
+
src={props.src}
|
|
123
|
+
alt={props.alt}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
render(
|
|
127
|
+
<Image
|
|
128
|
+
as={CustomImage}
|
|
129
|
+
src="/img.png"
|
|
130
|
+
alt="Props test"
|
|
131
|
+
fill={true}
|
|
132
|
+
priority={true}
|
|
133
|
+
/>
|
|
134
|
+
);
|
|
135
|
+
const img = screen.getByTestId("custom-img");
|
|
136
|
+
expect(img).toHaveAttribute("data-fill", "true");
|
|
137
|
+
expect(img).toHaveAttribute("data-priority", "true");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("does not render native img when as prop is provided", () => {
|
|
141
|
+
const CustomImage = (props: any) => (
|
|
142
|
+
<div data-testid="custom-wrapper">{props.alt}</div>
|
|
143
|
+
);
|
|
144
|
+
render(<Image as={CustomImage} src="/img.png" alt="No native" />);
|
|
145
|
+
expect(screen.queryByRole("img")).not.toBeInTheDocument();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("renders forwardRef component via as prop", () => {
|
|
149
|
+
const ForwardRefImage = forwardRef<HTMLImageElement, ImageComponentProps>(
|
|
150
|
+
function ForwardRefImage(props, fwdRef) {
|
|
151
|
+
return <img ref={fwdRef} data-testid="fwdref-img" {...props} />;
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
render(
|
|
155
|
+
<Image as={ForwardRefImage} src="/img.png" alt="ForwardRef test" />
|
|
156
|
+
);
|
|
157
|
+
expect(screen.getByTestId("fwdref-img")).toBeInTheDocument();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("edge cases", () => {
|
|
162
|
+
it("renders with empty alt for decorative images", () => {
|
|
163
|
+
const { container } = render(<Image src="/decorative.png" alt="" />);
|
|
164
|
+
const img = container.querySelector("img");
|
|
165
|
+
expect(img).toHaveAttribute("alt", "");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("renders with no className (defaults to empty string)", () => {
|
|
169
|
+
const { container } = render(<Image src="/img.png" alt="No class" />);
|
|
170
|
+
const img = container.querySelector("img");
|
|
171
|
+
expect(img).toBeInTheDocument();
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { forwardRef } from "react";
|
|
4
|
-
|
|
5
|
-
import type { ImageProps } from "@shared/components/image/types";
|
|
6
|
-
import { cx } from "@shared/utils";
|
|
7
|
-
|
|
8
|
-
export const Image = forwardRef<HTMLImageElement, ImageProps>(
|
|
9
|
-
({ as: ImageComponent, className = "", ...rest }, ref) => {
|
|
10
|
-
const combinedClassName = cx(className);
|
|
11
|
-
|
|
12
|
-
if (ImageComponent) {
|
|
13
|
-
// Cast to any to allow ref forwarding for custom components
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
-
const Component = ImageComponent as any;
|
|
16
|
-
return <Component ref={ref} className={combinedClassName} {...rest} />;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return <img ref={ref} className={combinedClassName} {...rest} />;
|
|
20
|
-
}
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
Image.displayName = "Image";
|
|
24
|
-
|
|
25
|
-
export type { ImageProps, ImageComponentProps } from "./types";
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
|
|
5
|
+
import type { ImageProps } from "@shared/components/image/types";
|
|
6
|
+
import { cx } from "@shared/utils";
|
|
7
|
+
|
|
8
|
+
export const Image = forwardRef<HTMLImageElement, ImageProps>(
|
|
9
|
+
({ as: ImageComponent, className = "", ...rest }, ref) => {
|
|
10
|
+
const combinedClassName = cx(className);
|
|
11
|
+
|
|
12
|
+
if (ImageComponent) {
|
|
13
|
+
// Cast to any to allow ref forwarding for custom components
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
const Component = ImageComponent as any;
|
|
16
|
+
return <Component ref={ref} className={combinedClassName} {...rest} />;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return <img ref={ref} className={combinedClassName} {...rest} />;
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
Image.displayName = "Image";
|
|
24
|
+
|
|
25
|
+
export type { ImageProps, ImageComponentProps } from "./types";
|