@nuskin/foundation-ui-components 1.29.1 → 1.30.0
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/package.json +1 -7
- package/src/common/accordion/NsAccordion.tsx +2 -2
- package/src/common/accordion/NsAccordionHeader.tsx +2 -9
- package/src/common/accordion/NsAccordionItem.tsx +4 -3
- package/src/common/accordion/NsCollapse.tsx +1 -1
- package/src/common/accordion/NsToggleSection.tsx +10 -3
- package/src/common/accordion/stories/NsAccordionItem.stories.tsx +5 -3
- package/src/common/accordion/stories/NsCollapse.stories.tsx +5 -3
- package/src/common/alert/NsAlert.stories.tsx +6 -4
- package/src/common/alert/NsAlert.tsx +27 -13
- package/src/common/auth-dropdown/NsAuthDropdown.tsx +7 -7
- package/src/common/autocomplete/NsAutocomplete.spec.tsx +3 -3
- package/src/common/autocomplete/NsAutocomplete.tsx +2 -1
- package/src/common/autocomplete/helper/SeeMoreButton.tsx +2 -2
- package/src/common/autocomplete/helper/renderGroupName.tsx +4 -3
- package/src/common/autocomplete/stories/NsAutocomplete.stories.tsx +9 -9
- package/src/common/banner/NsBanner.spec.tsx +5 -5
- package/src/common/banner/NsBanner.stories.tsx +8 -5
- package/src/common/banner/NsBanner.tsx +10 -20
- package/src/common/banner/types.d.ts +2 -2
- package/src/common/brand-components/NsBrandComponent.tsx +6 -3
- package/src/common/brand-components/icons/NSLogoHorizontal.tsx +7 -2
- package/src/common/brand-components/icons/NSLogoMark.tsx +3 -1
- package/src/common/brand-components/icons/NSLogoPoweredBy.tsx +4 -2
- package/src/common/brand-components/icons/NSLogoStacked.tsx +4 -2
- package/src/common/brand-components/icons/NSWordMark.tsx +4 -2
- package/src/common/breadcrumb/NsBreadcrumb.tsx +20 -5
- package/src/common/button/NsButton.stories.tsx +14 -8
- package/src/common/button/NsButton.tsx +7 -4
- package/src/common/card/CardButton.tsx +4 -3
- package/src/common/card/NsAwardCard.tsx +9 -8
- package/src/common/card/NsBgCard.tsx +1 -0
- package/src/common/card/NsBrandCard.tsx +8 -5
- package/src/common/card/NsBrandWrapper.tsx +3 -2
- package/src/common/card/NsCheckoutCard.tsx +4 -2
- package/src/common/card/NsHeroCardLevel1.tsx +9 -9
- package/src/common/card/NsHeroCardLevel2.tsx +9 -9
- package/src/common/card/NsIconText.tsx +3 -2
- package/src/common/card/NsLargeCard.tsx +9 -9
- package/src/common/card/NsMediumCard.tsx +10 -9
- package/src/common/card/NsPost.tsx +7 -8
- package/src/common/card/NsSmallFullCard.tsx +9 -9
- package/src/common/card/NsXLargeCard.tsx +9 -9
- package/src/common/card/fixtures/ImageData1.ts +38 -0
- package/src/common/card/fixtures/ImageData2.ts +38 -0
- package/src/common/card/specs/NsAwardCard.spec.tsx +2 -2
- package/src/common/card/specs/NsHeroCardLevel1.spec.tsx +2 -3
- package/src/common/card/specs/NsHeroCardLevel2.spec.tsx +2 -2
- package/src/common/card/specs/NsLargeCard.spec.tsx +2 -3
- package/src/common/card/specs/NsMediumCard.spec.tsx +3 -3
- package/src/common/card/specs/NsPost.spec.tsx +3 -3
- package/src/common/card/specs/NsSmallFullCard.spec.tsx +3 -3
- package/src/common/card/specs/NsXLargeCard.spec.tsx +3 -3
- package/src/common/card/stories/NsAwardCard.stories.tsx +2 -4
- package/src/common/card/stories/NsHeroCardLevel1.stories.tsx +2 -4
- package/src/common/card/stories/NsHeroCardLevel2.stories.tsx +2 -4
- package/src/common/card/stories/NsLargeCard.stories.tsx +2 -4
- package/src/common/card/stories/NsMediumCard.stories.tsx +2 -4
- package/src/common/card/stories/NsPost.stories.tsx +2 -4
- package/src/common/card/stories/NsSmallFullCard.stories.tsx +2 -4
- package/src/common/card/stories/NsXLargeCard.stories.tsx +4 -5
- package/src/common/carousal/CarousalControl.tsx +5 -2
- package/src/common/carousal/IconWrapper.tsx +4 -3
- package/src/common/carousal/NsCarousal.tsx +8 -6
- package/src/common/carousal/NsScrollingCarousal.tsx +2 -2
- package/src/common/carousal/RenderBrand.tsx +3 -1
- package/src/common/carousal/specs/NsScrollingCarousal.spec.tsx +4 -2
- package/src/common/carousal/stories/{NSCarousal.stories.tsx → NsCarousal1.stories.tsx} +5 -2
- package/src/common/carousal/stories/NsScrollingCarousal.stories.tsx +4 -2
- package/src/common/cart-icon/NsCartIcon.tsx +9 -1
- package/src/common/checkbox/NsCheckbox.stories.tsx +5 -4
- package/src/common/checkbox/NsCheckbox.tsx +6 -6
- package/src/common/column-control/NsColumnControl.tsx +5 -3
- package/src/common/column-control/NsFlexColumns.tsx +10 -3
- package/src/common/column-control/NsGridColumns.tsx +10 -3
- package/src/common/column-control/stories/NsColumnControl.stories.tsx +8 -5
- package/src/common/column-control/stories/NsFlexColumns.stories.tsx +9 -6
- package/src/common/column-control/stories/NsGridColumns.stories.tsx +19 -7
- package/src/common/column-control/styled/common.ts +1 -1
- package/src/common/dropdown/NsDropdown.tsx +6 -6
- package/src/common/dropdown/NsDropdownOption.tsx +4 -3
- package/src/common/dropdown/stories/NsDropdown.stories.tsx +5 -3
- package/src/common/email-simple/NsEmailSimple.tsx +4 -3
- package/src/common/flag/NsFlag.tsx +3 -2
- package/src/common/hooks/useAccordionMap.tsx +1 -1
- package/src/common/icon/NsIcon.tsx +2 -5
- package/src/common/icon/icons/AlertErrorIcon.tsx +3 -1
- package/src/common/icon/icons/ArrowDown.tsx +7 -2
- package/src/common/icon/icons/ArrowLeft.tsx +7 -2
- package/src/common/icon/icons/ArrowRight.tsx +7 -2
- package/src/common/icon/icons/ArrowUp.tsx +4 -2
- package/src/common/icon/icons/Cart.tsx +7 -2
- package/src/common/icon/icons/CartBadge.tsx +7 -2
- package/src/common/icon/icons/Check.tsx +7 -2
- package/src/common/icon/icons/CheckShield.tsx +4 -2
- package/src/common/icon/icons/CheckVerified.tsx +7 -2
- package/src/common/icon/icons/ChevronRight.tsx +9 -4
- package/src/common/icon/icons/CloseIcon.tsx +7 -2
- package/src/common/icon/icons/ErrorIcon.tsx +3 -1
- package/src/common/icon/icons/HamburgerIcon.tsx +4 -1
- package/src/common/icon/icons/InfoFilled.tsx +7 -2
- package/src/common/icon/icons/InfoOutlined.tsx +7 -2
- package/src/common/icon/icons/RoundedClose.tsx +7 -2
- package/src/common/icon/icons/SearchIcon.tsx +3 -1
- package/src/common/icon/icons/SlashIcon.tsx +7 -2
- package/src/common/icon/icons/SuccessIcon.tsx +3 -1
- package/src/common/icon/icons/TrashIcon.tsx +3 -1
- package/src/common/icon/icons/UserIcon.tsx +4 -1
- package/src/common/icon/icons/UserOutline.tsx +3 -1
- package/src/common/icon/icons/WarningIcon.tsx +3 -1
- package/src/common/icon/icons/home.tsx +4 -2
- package/src/common/image/BynderMedia.spec.tsx +42 -0
- package/src/common/image/BynderMedia.tsx +48 -0
- package/src/common/image/NsImage.tsx +3 -3
- package/src/common/image/NsNextImage.tsx +5 -2
- package/src/common/image/bynder-util.spec.ts +187 -0
- package/src/common/image/bynder-util.ts +98 -0
- package/src/common/image/index.ts +1 -1
- package/src/common/image/types.ts +78 -0
- package/src/common/input/InputWrapper.tsx +2 -1
- package/src/common/input/NsInputField.tsx +11 -3
- package/src/common/label/NsLabel.tsx +9 -4
- package/src/common/link/NsLink.stories.tsx +9 -5
- package/src/common/link/NsLink.tsx +9 -3
- package/src/common/modal/NsModal.tsx +2 -2
- package/src/common/modal/helper-component/NsModalContentBox.tsx +8 -3
- package/src/common/modal/helper-component/NsModalDefault.tsx +9 -2
- package/src/common/modal/helper-component/NsModalStatus.tsx +3 -1
- package/src/common/modal/helper-component/NsModalStatusIcon.tsx +2 -2
- package/src/common/modal/helper-component/NsModalTitle.tsx +6 -3
- package/src/common/modal/stories/NsModal.stories.tsx +9 -7
- package/src/common/nav-product-card/NsNavProductCard.spec.tsx +3 -5
- package/src/common/nav-product-card/NsNavProductCard.stories.tsx +4 -4
- package/src/common/nav-product-card/NsNavProductCard.tsx +6 -6
- package/src/common/nav-product-card/types.d.ts +2 -1
- package/src/common/nav-tab/NavDrawer.tsx +10 -3
- package/src/common/nav-tab/NavTitles.tsx +2 -2
- package/src/common/nav-tab/NsNavTab.tsx +10 -4
- package/src/common/progress/NsProgress.stories.tsx +6 -3
- package/src/common/progress/NsProgress.tsx +3 -3
- package/src/common/radiogroup/NsRadioGroup.tsx +4 -2
- package/src/common/search-bar/NsSearchBar.tsx +3 -2
- package/src/common/select/DefaultRenderOption.tsx +3 -1
- package/src/common/select/NsSelect.tsx +19 -6
- package/src/common/select/NsSelectOption.tsx +2 -2
- package/src/common/select/stories/NsSelect.stories.tsx +5 -4
- package/src/common/spinner/NsSpinner.stories.tsx +5 -2
- package/src/common/spinner/NsSpinner.tsx +3 -2
- package/src/common/spinner/icons/Spinner.tsx +3 -1
- package/src/common/table/NsExpandableRow.tsx +35 -41
- package/src/common/table/NsTable.stories.tsx +17 -23
- package/src/common/table/NsTable.tsx +27 -10
- package/src/common/table/NsTableHeader.tsx +18 -5
- package/src/common/table/NsTablePagination.tsx +7 -11
- package/src/common/text-strip/NsTextStrip.tsx +6 -3
- package/src/common/toast/Fade.tsx +5 -8
- package/src/common/toast/ToastProvider.tsx +18 -17
- package/src/common/toast/specs/Toast.spec.tsx +56 -64
- package/src/common/toast/stories/Toast.stories.tsx +12 -11
- package/src/common/typography/GlobalFontInter.tsx +7 -2
- package/src/common/typography/NsTypography.tsx +11 -4
- package/src/common/videos/NsVideo.tsx +5 -2
- package/src/common/image/types.d.ts +0 -30
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
1
3
|
/* eslint-disable max-len */
|
|
2
|
-
import {
|
|
4
|
+
import { SvgIcon, SvgIconProps } from '@mui/material';
|
|
3
5
|
|
|
4
|
-
const Home = (props: SvgIconProps) => {
|
|
6
|
+
const Home = (props: SvgIconProps): ReactElement => {
|
|
5
7
|
return (
|
|
6
8
|
<SvgIcon viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" data-testid="home" {...props}>
|
|
7
9
|
<path
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { BynderMedia } from './BynderMedia';
|
|
4
|
+
import { ImageData1 } from '../card/fixtures/ImageData1';
|
|
5
|
+
|
|
6
|
+
describe('BynderMedia', () => {
|
|
7
|
+
const imageDetail = ImageData1;
|
|
8
|
+
const suffix = 'mockSuffix';
|
|
9
|
+
const className = 'mockClassName';
|
|
10
|
+
const testId = 'mockTestId';
|
|
11
|
+
const liveTag = { 'data-cslp': 'cslp' };
|
|
12
|
+
|
|
13
|
+
it('1. renders video element for type VIDEO', () => {
|
|
14
|
+
const selected = { url: 'mockVideoUrl', type: 'VIDEO' };
|
|
15
|
+
const previewUrls = ['mockPreviewUrl1', 'mockPreviewUrl2'];
|
|
16
|
+
const isPublic = true;
|
|
17
|
+
const { container } = render(
|
|
18
|
+
<BynderMedia
|
|
19
|
+
imageDetail={{ ...imageDetail, files: {}, selected: selected as any, previewUrls, isPublic }}
|
|
20
|
+
suffix={suffix}
|
|
21
|
+
className={className}
|
|
22
|
+
testId={testId}
|
|
23
|
+
liveTag={liveTag}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
expect(container.querySelector('video')).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('2. renders default element for non-VIDEO type', () => {
|
|
30
|
+
const { container } = render(
|
|
31
|
+
<BynderMedia
|
|
32
|
+
imageDetail={imageDetail}
|
|
33
|
+
suffix={suffix}
|
|
34
|
+
className={className}
|
|
35
|
+
testId={testId}
|
|
36
|
+
liveTag={liveTag}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
expect(container.querySelector('video')).not.toBeInTheDocument();
|
|
40
|
+
expect(container.querySelector('img')).toBeInTheDocument();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
import { CslpTag } from '../common.types';
|
|
4
|
+
/* eslint-disable @next/next/no-img-element */
|
|
5
|
+
import { getImageType, getMediaUrl, getUrlBasedOnDerivative } from './bynder-util';
|
|
6
|
+
import { BynderImageDataType } from './types';
|
|
7
|
+
|
|
8
|
+
type BynderMediaProps = {
|
|
9
|
+
imageDetail: BynderImageDataType;
|
|
10
|
+
suffix?: string;
|
|
11
|
+
className?: string;
|
|
12
|
+
testId?: string;
|
|
13
|
+
liveTag?: CslpTag;
|
|
14
|
+
};
|
|
15
|
+
export const BynderMedia = ({ imageDetail, suffix, className, testId, liveTag }: BynderMediaProps): ReactElement => {
|
|
16
|
+
const { files, name, selected, previewUrls, isPublic } = imageDetail;
|
|
17
|
+
const { url = '', fit, position, altText = '', bgColor, type } = selected ?? {};
|
|
18
|
+
const imageDerivativeType = getImageType(url, name, files);
|
|
19
|
+
const mediaUrl = getMediaUrl(selected, isPublic, previewUrls);
|
|
20
|
+
let transformedMediaUrl = getUrlBasedOnDerivative(mediaUrl, imageDerivativeType, suffix ?? '');
|
|
21
|
+
if (type === 'VIDEO') {
|
|
22
|
+
return (
|
|
23
|
+
<video
|
|
24
|
+
loop
|
|
25
|
+
autoPlay
|
|
26
|
+
muted
|
|
27
|
+
width="100%"
|
|
28
|
+
{...liveTag}
|
|
29
|
+
style={{ backgroundColor: bgColor, objectFit: fit, objectPosition: position }}
|
|
30
|
+
className={`${className || ''} bynder-image`}
|
|
31
|
+
data-testid={testId || 'test-bynder-video'}
|
|
32
|
+
>
|
|
33
|
+
<source src={transformedMediaUrl} type="video/mp4" />
|
|
34
|
+
<source src={transformedMediaUrl} type="video/ogg" />
|
|
35
|
+
</video>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return (
|
|
39
|
+
<img
|
|
40
|
+
{...liveTag}
|
|
41
|
+
data-testid={testId || 'test-bynder-image'}
|
|
42
|
+
className={`${className || ''} bynder-image`}
|
|
43
|
+
src={transformedMediaUrl}
|
|
44
|
+
alt={altText}
|
|
45
|
+
style={{ backgroundColor: bgColor, objectFit: fit, objectPosition: position }}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
2
|
|
|
3
|
-
import { NsImageProps } from './types
|
|
3
|
+
import { NsImageProps } from './types';
|
|
4
4
|
|
|
5
5
|
// eslint-disable-next-line no-unused-vars
|
|
6
|
-
const NsImage = ({ src, width, height, alt, variant, ...rest }: NsImageProps):
|
|
6
|
+
const NsImage = ({ src, width, height, alt, variant, ...rest }: NsImageProps): ReactElement => {
|
|
7
7
|
let cHeight, cWidth;
|
|
8
8
|
switch (variant) {
|
|
9
9
|
case 'product':
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
2
3
|
import Image from 'next/image';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
import { NsNextImageProps } from './types';
|
|
6
|
+
|
|
7
|
+
const NsNextImage = ({ src, width, height, alt, fill, priority, ...props }: NsNextImageProps): ReactElement => {
|
|
5
8
|
return <Image src={src} width={width} height={height} alt={alt} fill={fill} priority={priority} {...props} />;
|
|
6
9
|
};
|
|
7
10
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getBynderAltText,
|
|
3
|
+
extractBynderDescription,
|
|
4
|
+
getBasicDATWidthHeight,
|
|
5
|
+
parseJsonString,
|
|
6
|
+
getImageType,
|
|
7
|
+
getUrlBasedOnDerivative,
|
|
8
|
+
getMediaUrl,
|
|
9
|
+
} from './bynder-util';
|
|
10
|
+
import { ImageType } from './types';
|
|
11
|
+
|
|
12
|
+
describe('A. bynder-util.getBasicDATWidthHeight', () => {
|
|
13
|
+
it('1. should return an scale transform with given height and width', () => {
|
|
14
|
+
const result = getBasicDATWidthHeight(500, 600);
|
|
15
|
+
expect(result).toEqual('io=transform:scale,width:500,height:600');
|
|
16
|
+
});
|
|
17
|
+
it('2. should return an fill transform with given height and width', () => {
|
|
18
|
+
const result = getBasicDATWidthHeight(500, 600, 'fill');
|
|
19
|
+
expect(result).toEqual('io=transform:fill,width:500,height:600');
|
|
20
|
+
});
|
|
21
|
+
it('3. should return a url with default width and height when default value is missing', () => {
|
|
22
|
+
const result = getBasicDATWidthHeight();
|
|
23
|
+
expect(result).toEqual('io=transform:scale,width:900,height:800');
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('B. bynder-util.parseJsonString', () => {
|
|
28
|
+
it('1. should successfully parse a valid JSON string', () => {
|
|
29
|
+
const jsonString = '{"name": "John", "age": 30}';
|
|
30
|
+
expect(parseJsonString(jsonString)).toEqual({ name: 'John', age: 30 });
|
|
31
|
+
});
|
|
32
|
+
it('2. should return an empty object when parsing an empty string', () => {
|
|
33
|
+
const jsonString = '';
|
|
34
|
+
expect(parseJsonString(jsonString)).toEqual({});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('C. bynder-util.getBynderAltText', () => {
|
|
39
|
+
it('1. should return the value of textMetaproperties if available', () => {
|
|
40
|
+
const obj = {
|
|
41
|
+
image: [{ description: 'description', textMetaproperties: [{ name: 'SEO_Tags', value: 'SEO tag value' }] }],
|
|
42
|
+
};
|
|
43
|
+
expect(getBynderAltText('image')(obj)).toEqual('SEO tag value');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('2. should return an empty string if textMetaproperties is not available', () => {
|
|
47
|
+
const obj = {};
|
|
48
|
+
expect(getBynderAltText('image')(obj)).toEqual('');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('D. bynder-util.extractBynderDescription', () => {
|
|
53
|
+
it('1. should return the description if available', () => {
|
|
54
|
+
const obj = { image: [{ description: 'description' }] };
|
|
55
|
+
expect(extractBynderDescription('image')(obj)).toEqual('description');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('2. should return an empty string if description is not available', () => {
|
|
59
|
+
const obj = {};
|
|
60
|
+
expect(extractBynderDescription('image')(obj)).toEqual('');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('E. bynder-util.getImageType', () => {
|
|
65
|
+
it('1. should return transformBaseUrl if url contains transformBaseUrl', () => {
|
|
66
|
+
const url =
|
|
67
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1';
|
|
68
|
+
const fileName = 'mail-40th-240207-1';
|
|
69
|
+
const files = {
|
|
70
|
+
transformBaseUrl: {
|
|
71
|
+
url: 'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
expect(getImageType(url, fileName, files)).toEqual(ImageType.transformBaseUrl);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('2. should return customTransformation if url contains customTransformation', () => {
|
|
78
|
+
const url =
|
|
79
|
+
'https://media.nuskin.com/transform/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1?io=overlay:text,content:abc,font:Open Sans,size:64,color:FFFFFFFF,gravity:center,padding:20,angle:0';
|
|
80
|
+
const fileName = 'mail-40th-240207-1';
|
|
81
|
+
const files = {
|
|
82
|
+
customTransformation: {
|
|
83
|
+
url: 'https://media.nuskin.com/transform/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
expect(getImageType(url, fileName, files)).toEqual(ImageType.customTransformation);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('3. should return key if present in file', () => {
|
|
90
|
+
const url =
|
|
91
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1';
|
|
92
|
+
const fileName = 'mail-40th-240207-1';
|
|
93
|
+
const files = {
|
|
94
|
+
base: {
|
|
95
|
+
url: 'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
expect(getImageType(url, fileName, files)).toEqual('base');
|
|
99
|
+
});
|
|
100
|
+
it('4. should return defaultPreset if key not present in files', () => {
|
|
101
|
+
const url =
|
|
102
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1';
|
|
103
|
+
const fileName = 'mail-40th-240207-1';
|
|
104
|
+
const files = {
|
|
105
|
+
base: {
|
|
106
|
+
url: 'https://media.nuskin.com/transform/s_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1',
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
expect(getImageType(url, fileName, files)).toEqual(ImageType.defaultPreset);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('F. bynder-util.getUrlBasedOnDerivative', () => {
|
|
114
|
+
it('1. should append suffix to custom transformed url', () => {
|
|
115
|
+
const url =
|
|
116
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1?io=overlay:text,content:abc,font:Open Sans,size:64,color:FFFFFFFF,gravity:center,padding:20,angle:0';
|
|
117
|
+
const suffix = 'io=transform:fill,width:90,height:90';
|
|
118
|
+
const derivedUrl = getUrlBasedOnDerivative(url, ImageType.customTransformation, suffix);
|
|
119
|
+
expect(derivedUrl).toEqual(`${url}&${suffix}`);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('2. should append suffix to base url', () => {
|
|
123
|
+
const url =
|
|
124
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1';
|
|
125
|
+
const suffix = 'io=transform:fill,width:90,height:90';
|
|
126
|
+
const derivedUrl = getUrlBasedOnDerivative(url, ImageType.transformBaseUrl, suffix);
|
|
127
|
+
expect(derivedUrl).toEqual(`${url}?${suffix}`);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('3. should return url as it is', () => {
|
|
131
|
+
const url =
|
|
132
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1';
|
|
133
|
+
const suffix = 'io=transform:fill,width:90,height:90';
|
|
134
|
+
const derivedUrl = getUrlBasedOnDerivative(url, ImageType.defaultPreset, suffix);
|
|
135
|
+
expect(derivedUrl).toEqual(url);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('G. bynder-util.getMediaUrl', () => {
|
|
140
|
+
it('1. should return url if asset is public', () => {
|
|
141
|
+
const url =
|
|
142
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1';
|
|
143
|
+
const isPublic = true;
|
|
144
|
+
const previewUrls = [
|
|
145
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1',
|
|
146
|
+
];
|
|
147
|
+
const mediaUrl = getMediaUrl({ url } as any, isPublic, previewUrls);
|
|
148
|
+
expect(mediaUrl).toEqual(url);
|
|
149
|
+
});
|
|
150
|
+
it('2. should return Url if asset is not public and filetype not mentioned', () => {
|
|
151
|
+
const url =
|
|
152
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1';
|
|
153
|
+
const isPublic = false;
|
|
154
|
+
const previewUrls = [
|
|
155
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1',
|
|
156
|
+
];
|
|
157
|
+
const mediaUrl = getMediaUrl({ url } as any, isPublic, previewUrls);
|
|
158
|
+
expect(mediaUrl).toEqual(url);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('3. should return previewUrl if asset is video and is not public', () => {
|
|
162
|
+
const url =
|
|
163
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1';
|
|
164
|
+
const isPublic = false;
|
|
165
|
+
const previewUrls = [
|
|
166
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1',
|
|
167
|
+
];
|
|
168
|
+
const mediaUrl = getMediaUrl({ url, type: 'VIDEO' } as any, isPublic, previewUrls);
|
|
169
|
+
expect(mediaUrl).toEqual(previewUrls[0]);
|
|
170
|
+
});
|
|
171
|
+
it('4. should return url if asset is video and is public', () => {
|
|
172
|
+
const url =
|
|
173
|
+
'https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1';
|
|
174
|
+
const isPublic = true;
|
|
175
|
+
const previewUrls = [''];
|
|
176
|
+
const mediaUrl = getMediaUrl({ url, type: 'VIDEO' } as any, isPublic, previewUrls);
|
|
177
|
+
expect(mediaUrl).toEqual(url);
|
|
178
|
+
});
|
|
179
|
+
it('5. should return empty string if selected is empty object', () => {
|
|
180
|
+
const mediaUrl = getMediaUrl({} as any, false, []);
|
|
181
|
+
expect(mediaUrl).toEqual('');
|
|
182
|
+
});
|
|
183
|
+
it('6. should return empty string if selected is undefined', () => {
|
|
184
|
+
const mediaUrl = getMediaUrl(undefined, false, []);
|
|
185
|
+
expect(mediaUrl).toEqual('');
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import compose from 'ramda/src/compose';
|
|
3
|
+
import find from 'ramda/src/find';
|
|
4
|
+
import pathOr from 'ramda/src/pathOr';
|
|
5
|
+
import propEq from 'ramda/src/propEq';
|
|
6
|
+
import { BynderImageDataType, ImageType } from './types';
|
|
7
|
+
|
|
8
|
+
export const parseJsonString = (dataString: string) => {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(dataString);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.error('error parsing data ', error);
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** alt text */
|
|
18
|
+
export const getBynderAltText = (propKey: string) =>
|
|
19
|
+
compose(pathOr('', ['value']), find(propEq('SEO_Tags', 'name')), pathOr([], [propKey, 0, 'textMetaproperties']));
|
|
20
|
+
|
|
21
|
+
export const extractBynderDescription = (key: string) => pathOr('', [key, '0', 'description']);
|
|
22
|
+
|
|
23
|
+
/** end alt text */
|
|
24
|
+
|
|
25
|
+
/** DAT specific */
|
|
26
|
+
|
|
27
|
+
export const getBasicDATWidthHeight = (width: number = 900, height: number = 800, transform = 'scale') =>
|
|
28
|
+
`io=transform:${transform},width:${width},height:${height}`;
|
|
29
|
+
|
|
30
|
+
/** determine whether the image is
|
|
31
|
+
* 1. custom transformation ('customTransformation') ==> further transformation cannot be applied
|
|
32
|
+
https://media.nuskin.com/transform/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1?io=overlay:text,content:abc,font:Open Sans,size:64,color:FFFFFFFF,gravity:center,padding:20,angle:0
|
|
33
|
+
|
|
34
|
+
* 2. base transform url ('transformBaseUrl') ==> transformation can be applied in the component
|
|
35
|
+
* "https://media.nuskin.com/transform/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1"
|
|
36
|
+
|
|
37
|
+
* 3. bynder defined preset ('preset') ==> further transformation cannot be applied
|
|
38
|
+
"https://media.nuskin.com/transform/PDP_Key_Ingredients_Image/3ea706a3-770e-4b66-b112-427ef840f4b6/mail-40th-240207-1"
|
|
39
|
+
|
|
40
|
+
* 4. custom preset ('defaultPreset') example: thumbnail, small, mini, original , webImage, ==> further transformation can be applied
|
|
41
|
+
|
|
42
|
+
* returns either 'customTransformation' | 'transformBaseUrl' | or preset type of the image example webImage, thumbnail
|
|
43
|
+
* */
|
|
44
|
+
export const getImageType = (url: string, fileName: string, files: Record<string, any>) => {
|
|
45
|
+
if (url.includes(`${fileName}?io=`)) {
|
|
46
|
+
return ImageType.customTransformation;
|
|
47
|
+
} else {
|
|
48
|
+
const parts = url.split('/');
|
|
49
|
+
if (parts[3] === 'transform' && parts[5] === fileName) {
|
|
50
|
+
// url?.toLowerCase().includes(`transform/${databaseId}/${fileName}`.toLowerCase()) this check creates error in sometimes url does not have - which is present in database id
|
|
51
|
+
|
|
52
|
+
return ImageType.transformBaseUrl;
|
|
53
|
+
} else {
|
|
54
|
+
return retrieveImageKey(files, url);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
export const retrieveImageKey = (files: Record<string, any>, url: string) => {
|
|
59
|
+
for (const key in files) {
|
|
60
|
+
if (files[key].url === url) {
|
|
61
|
+
return key;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return ImageType.defaultPreset;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const getUrlBasedOnDerivative = (url: string, imageDerivativeType: string, suffix: string) => {
|
|
68
|
+
if (imageDerivativeType === ImageType.customTransformation) {
|
|
69
|
+
return `${url}&${suffix}`;
|
|
70
|
+
} else if (imageDerivativeType === ImageType.transformBaseUrl) {
|
|
71
|
+
return `${url}?${suffix}`;
|
|
72
|
+
}
|
|
73
|
+
return url;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** end DAT */
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
*
|
|
80
|
+
* @param selected : object containing select image url
|
|
81
|
+
* @param isPublic : whether asset is marked public, if it is video and marked public, then mov or gif can be selected otherwise, only webImage can be selected in that case we have to show previewUrl
|
|
82
|
+
* @returns returns selected video or image, if asset is video and is not public, then returns previewUrl
|
|
83
|
+
*/
|
|
84
|
+
export function getMediaUrl(
|
|
85
|
+
selected: BynderImageDataType['selected'],
|
|
86
|
+
isPublic: boolean,
|
|
87
|
+
previewUrls: BynderImageDataType['previewUrls']
|
|
88
|
+
): string {
|
|
89
|
+
const { url = '', type: fileType = '' } = selected ?? {};
|
|
90
|
+
if (fileType === 'VIDEO') {
|
|
91
|
+
if (isPublic) {
|
|
92
|
+
return url;
|
|
93
|
+
} else {
|
|
94
|
+
return previewUrls?.[0] || '';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return url;
|
|
98
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import { ImageProps } from 'next/image';
|
|
3
|
+
|
|
4
|
+
export type NsNextImageProps = Pick<
|
|
5
|
+
ImageProps,
|
|
6
|
+
| 'src'
|
|
7
|
+
| 'height'
|
|
8
|
+
| 'width'
|
|
9
|
+
| 'alt'
|
|
10
|
+
| 'priority'
|
|
11
|
+
| 'sizes'
|
|
12
|
+
| 'quality'
|
|
13
|
+
| 'placeholder'
|
|
14
|
+
| 'style'
|
|
15
|
+
| 'loading'
|
|
16
|
+
| 'blurDataURL'
|
|
17
|
+
| 'fill'
|
|
18
|
+
| 'unoptimized'
|
|
19
|
+
| 'loader'
|
|
20
|
+
> & { id?: string; className?: string };
|
|
21
|
+
|
|
22
|
+
export type NsImageProps = {
|
|
23
|
+
alt: string;
|
|
24
|
+
className?: string;
|
|
25
|
+
fill?: boolean;
|
|
26
|
+
height?: number | string;
|
|
27
|
+
id?: string;
|
|
28
|
+
src: string;
|
|
29
|
+
variant?: 'product' | 'award' | 'icon' | 'fill';
|
|
30
|
+
width?: number | string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** types */
|
|
34
|
+
export type DATOptions = { width?: number; height?: number; transform: 'fill' | 'scale' };
|
|
35
|
+
export type BynderImageDataType = {
|
|
36
|
+
/** selected is manually added, using additionalInfo from bynder */
|
|
37
|
+
selected?: {
|
|
38
|
+
url: string;
|
|
39
|
+
fileSize: number | null;
|
|
40
|
+
width: number | null;
|
|
41
|
+
height: number | null;
|
|
42
|
+
imageType?: string;
|
|
43
|
+
type: 'VIDEO' | 'IMAGE';
|
|
44
|
+
altText?: string;
|
|
45
|
+
fit: 'cover' | 'contain' | 'scale-down' | 'none';
|
|
46
|
+
position: string;
|
|
47
|
+
bgColor: string;
|
|
48
|
+
};
|
|
49
|
+
isPublic: boolean;
|
|
50
|
+
__typename?: 'Image';
|
|
51
|
+
id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
description: string | null;
|
|
54
|
+
databaseId: string;
|
|
55
|
+
originalUrl?: string;
|
|
56
|
+
type: 'VIDEO' | 'IMAGE' | 'DOCUMENT';
|
|
57
|
+
url?: string;
|
|
58
|
+
previewUrls?: string[];
|
|
59
|
+
extensions?: string[];
|
|
60
|
+
textMetaproperties?: ({
|
|
61
|
+
name: string;
|
|
62
|
+
value: string;
|
|
63
|
+
} | null)[];
|
|
64
|
+
files: {
|
|
65
|
+
[key: string]: {
|
|
66
|
+
url: string;
|
|
67
|
+
width: number | null;
|
|
68
|
+
height: number | null;
|
|
69
|
+
fileSize: number | null;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export enum ImageType {
|
|
75
|
+
defaultPreset = 'defaultPreset',
|
|
76
|
+
customTransformation = 'customTransformation',
|
|
77
|
+
transformBaseUrl = 'transformBaseUrl',
|
|
78
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Box } from '@mui/material';
|
|
2
|
-
import {
|
|
2
|
+
import { ColorUtils, FoundationTheme, nuskinTheme, styled } from '@nuskin/foundation-theme';
|
|
3
|
+
|
|
3
4
|
import { fieldStateType } from './types.d';
|
|
4
5
|
|
|
5
6
|
const { isDarkMode } = ColorUtils;
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
FormHelperText,
|
|
5
|
+
TextField,
|
|
6
|
+
} from '@mui/material';
|
|
2
7
|
|
|
3
8
|
import { NsLabel } from '../label';
|
|
4
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
AdornmentPropsType,
|
|
11
|
+
getAdornmentProps,
|
|
12
|
+
} from './getAdornmentProps';
|
|
5
13
|
import InputFieldWrapper from './InputFieldWrapper';
|
|
6
14
|
import InputWrapper from './InputWrapper';
|
|
7
15
|
import { NsInputFieldProps } from './types.d';
|
|
@@ -11,7 +19,7 @@ import { NsInputFieldProps } from './types.d';
|
|
|
11
19
|
* @param props { placeholder, fieldState, iconStart, iconEnd, iconSide }
|
|
12
20
|
* @returns Input Field
|
|
13
21
|
*/
|
|
14
|
-
const NsInputField = (props: NsInputFieldProps) => {
|
|
22
|
+
const NsInputField = (props: NsInputFieldProps): ReactElement => {
|
|
15
23
|
const {
|
|
16
24
|
autoComplete,
|
|
17
25
|
autoFocus,
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CSSProperties,
|
|
3
|
+
ReactElement,
|
|
4
|
+
} from 'react';
|
|
2
5
|
|
|
3
6
|
import { FormLabel } from '@mui/material';
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
import {
|
|
8
|
+
ColorUtils,
|
|
9
|
+
styled,
|
|
10
|
+
} from '@nuskin/foundation-theme';
|
|
6
11
|
|
|
7
12
|
import { NsLabelProps } from './types.d';
|
|
8
13
|
|
|
@@ -26,7 +31,7 @@ const StyledLabel = styled(FormLabel)<{ fontSize?: number; style?: CSSProperties
|
|
|
26
31
|
} as any; // TODO: may be this "any" has to be fixed from foundation-theme
|
|
27
32
|
});
|
|
28
33
|
|
|
29
|
-
const NsLabel = (props: NsLabelProps) => {
|
|
34
|
+
const NsLabel = (props: NsLabelProps): ReactElement => {
|
|
30
35
|
const { required, showOptional, children, size } = props;
|
|
31
36
|
return (
|
|
32
37
|
<StyledLabel fontSize={size} {...props} required={required}>
|
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
* @description NsLink component.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import React from 'react';
|
|
8
|
-
|
|
7
|
+
import React, { ReactElement } from 'react';
|
|
8
|
+
|
|
9
9
|
import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
|
|
10
|
+
import type {
|
|
11
|
+
Meta,
|
|
12
|
+
StoryObj,
|
|
13
|
+
} from '@storybook/react';
|
|
10
14
|
|
|
11
|
-
import { NsLink } from './';
|
|
12
15
|
import { NsTypography } from '../typography';
|
|
16
|
+
import { NsLink } from './';
|
|
13
17
|
|
|
14
18
|
type ComponentType = typeof NsLink;
|
|
15
19
|
type Story = StoryObj<ComponentType>;
|
|
@@ -35,7 +39,7 @@ export default {
|
|
|
35
39
|
} as MetaStory;
|
|
36
40
|
|
|
37
41
|
export const Links: Story = {
|
|
38
|
-
render: (args: any) => (
|
|
42
|
+
render: (args: any): ReactElement => (
|
|
39
43
|
<NsTypography variant="title-l">
|
|
40
44
|
<NsLink {...args} href={args.href} />
|
|
41
45
|
</NsTypography>
|
|
@@ -43,7 +47,7 @@ export const Links: Story = {
|
|
|
43
47
|
};
|
|
44
48
|
|
|
45
49
|
export const ReactComponentLink: Story = {
|
|
46
|
-
render: (args: any) => (
|
|
50
|
+
render: (args: any):ReactElement => (
|
|
47
51
|
<NsLink {...args} href={args.href} passHref>
|
|
48
52
|
<NsTypography variant="title-l">Test Link</NsTypography>
|
|
49
53
|
</NsLink>
|
|
@@ -3,12 +3,18 @@
|
|
|
3
3
|
* @description Exports one components - NsLink
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React, {
|
|
6
|
+
import React, {
|
|
7
|
+
PropsWithChildren,
|
|
8
|
+
ReactElement,
|
|
9
|
+
} from 'react';
|
|
7
10
|
|
|
8
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
StyledLink,
|
|
13
|
+
StyledNextLink,
|
|
14
|
+
} from './NsLink.styled';
|
|
9
15
|
import { NsLinkProps } from './types.d';
|
|
10
16
|
|
|
11
|
-
const NsLink = (props: PropsWithChildren<NsLinkProps>) => {
|
|
17
|
+
const NsLink = (props: PropsWithChildren<NsLinkProps>): ReactElement => {
|
|
12
18
|
const {
|
|
13
19
|
className,
|
|
14
20
|
href,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { ReactElement } from 'react';
|
|
2
2
|
|
|
3
3
|
import Dialog from '@mui/material/Dialog';
|
|
4
4
|
|
|
5
5
|
import { ModalVariantMap } from './model/NsModalVariantMap';
|
|
6
6
|
import { NsModalProps } from './types';
|
|
7
7
|
|
|
8
|
-
const NsModal = (props: NsModalProps) => {
|
|
8
|
+
const NsModal = (props: NsModalProps): ReactElement => {
|
|
9
9
|
const {
|
|
10
10
|
open,
|
|
11
11
|
handleClose,
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ReactElement,
|
|
3
|
+
useMemo,
|
|
4
|
+
} from 'react';
|
|
5
|
+
|
|
2
6
|
import { DialogContent } from '@mui/material';
|
|
7
|
+
|
|
8
|
+
import { NsTypography } from '../../';
|
|
3
9
|
import { NsModalComponentProps } from '../types';
|
|
4
10
|
import NsModalTitle from './NsModalTitle';
|
|
5
|
-
import { NsTypography } from '../..';
|
|
6
11
|
|
|
7
|
-
const NsModalContentBox = ({ handleClose, statusInfo }: NsModalComponentProps) => {
|
|
12
|
+
const NsModalContentBox = ({ handleClose, statusInfo }: NsModalComponentProps): ReactElement => {
|
|
8
13
|
const { title, description } = statusInfo || {};
|
|
9
14
|
|
|
10
15
|
const header = useMemo(() => {
|