@nuskin/foundation-ui-components 1.0.0-cft-35.12 → 1.0.0-develop.1

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.
Files changed (101) hide show
  1. package/package.json +21 -16
  2. package/src/common/alert/NsAlert.spec.tsx +134 -0
  3. package/src/common/alert/NsAlert.stories.tsx +42 -0
  4. package/src/common/alert/NsAlert.styled.ts +124 -0
  5. package/src/common/alert/NsAlert.tsx +155 -0
  6. package/src/common/alert/index.ts +2 -0
  7. package/src/common/alert/types.d.ts +45 -0
  8. package/src/common/auth-dropdown/NsAuthDropdown.stories.tsx +9 -5
  9. package/src/common/auth-dropdown/NsAuthDropdown.tsx +1 -1
  10. package/src/common/autocomplete/NsAutocomplete.spec.tsx +96 -0
  11. package/src/common/autocomplete/NsAutocomplete.stories.tsx +51 -0
  12. package/src/common/autocomplete/NsAutocomplete.tsx +110 -0
  13. package/src/common/autocomplete/index.ts +1 -0
  14. package/src/common/autocomplete/mock/Mock.styled.tsx +23 -0
  15. package/src/common/autocomplete/mock/MockData.ts +46 -0
  16. package/src/common/autocomplete/mock/MockUI.tsx +24 -0
  17. package/src/common/autocomplete/types.ts +39 -0
  18. package/src/common/banner/NsBanner.spec.tsx +2 -2
  19. package/src/common/banner/NsBanner.stories.tsx +2 -2
  20. package/src/common/banner/NsBanner.styled.tsx +1 -0
  21. package/src/common/banner/NsBanner.tsx +12 -3
  22. package/src/common/brand-components/NsBrand.stories.tsx +2 -2
  23. package/src/common/breadcrumb/NsBreadcrumb.spec.tsx +12 -4
  24. package/src/common/breadcrumb/NsBreadcrumb.stories.tsx +19 -8
  25. package/src/common/breadcrumb/NsBreadcrumb.styled.tsx +1 -1
  26. package/src/common/breadcrumb/NsBreadcrumb.tsx +2 -4
  27. package/src/common/button/NsButton.stories.tsx +2 -2
  28. package/src/common/card/NsFfgCard.tsx +18 -0
  29. package/src/common/card/index.ts +1 -0
  30. package/src/common/card/stories/NsBgCard.stories.tsx +2 -2
  31. package/src/common/card/stories/NsBrandProductCard.stories.tsx +2 -2
  32. package/src/common/card/stories/NsFfgCard.stories.tsx +40 -0
  33. package/src/common/card/styled/NsFfgStyled.tsx +44 -0
  34. package/src/common/card/tests/NsFfgCard.spec.tsx +23 -0
  35. package/src/common/card/types.d.ts +9 -0
  36. package/src/common/carousal/CarousalControl.tsx +3 -5
  37. package/src/common/carousal/IconWrapper.tsx +4 -4
  38. package/src/common/carousal/tests/NsCarousal.spec.tsx +4 -4
  39. package/src/common/cart-icon/NsCartIcon.spec.tsx +10 -4
  40. package/src/common/cart-icon/NsCartIcon.stories.tsx +2 -2
  41. package/src/common/checkbox/NsCheckbox.stories.tsx +12 -16
  42. package/src/common/column-control/NDivs.story.tsx +35 -0
  43. package/src/common/column-control/NsColumnControl.spec.tsx +24 -0
  44. package/src/common/column-control/NsColumnControl.stories.tsx +69 -0
  45. package/src/common/column-control/NsColumnControl.tsx +59 -0
  46. package/src/common/column-control/index.ts +2 -0
  47. package/src/common/column-control/types.d.ts +47 -0
  48. package/src/common/column-control/utils.ts +66 -0
  49. package/src/common/dropdown/NsDropdown.tsx +1 -1
  50. package/src/common/dropdown/stories/NsDropdown.stories.tsx +13 -9
  51. package/src/common/dropdown/stories/NsDropdownCountry.stories.tsx +13 -9
  52. package/src/common/flag/NsFlag.stories.tsx +2 -2
  53. package/src/common/icon/NsIcon.spec.tsx +9 -2
  54. package/src/common/icon/NsIcon.stories.tsx +4 -17
  55. package/src/common/icon/NsIcon.tsx +1 -1
  56. package/src/common/icon/{IconMap.ts → NsIconVariants.ts} +10 -0
  57. package/src/common/icon/icons/AlertErrorIcon.tsx +36 -0
  58. package/src/common/icon/icons/ArrowDown.tsx +1 -1
  59. package/src/common/{carousal/LeftChevron.tsx → icon/icons/ArrowLeft.tsx} +7 -5
  60. package/src/common/{carousal/RightChevron.tsx → icon/icons/ArrowRight.tsx} +3 -3
  61. package/src/common/icon/icons/ArrowUp.tsx +1 -1
  62. package/src/common/icon/icons/Cart.tsx +7 -1
  63. package/src/common/icon/icons/CartBadge.tsx +7 -1
  64. package/src/common/icon/icons/CheckShield.tsx +7 -1
  65. package/src/common/icon/icons/CheckVerified.tsx +7 -1
  66. package/src/common/icon/icons/CloseIcon.tsx +22 -0
  67. package/src/common/icon/icons/ErrorIcon.tsx +1 -1
  68. package/src/common/icon/icons/InfoFilled.tsx +1 -1
  69. package/src/common/icon/icons/InfoOutlined.tsx +7 -1
  70. package/src/common/icon/icons/SearchIcon.tsx +1 -1
  71. package/src/common/icon/icons/SlashIcon.tsx +4 -4
  72. package/src/common/icon/icons/SuccessIcon.tsx +1 -1
  73. package/src/common/icon/icons/TrashIcon.tsx +1 -2
  74. package/src/common/icon/icons/WarningIcon.tsx +36 -0
  75. package/src/common/icon/icons/home.tsx +4 -3
  76. package/src/common/icon/index.ts +1 -0
  77. package/src/common/icon/types.d.ts +2 -15
  78. package/src/common/image/NsImage.stories.tsx +2 -2
  79. package/src/common/image/NsImage.tsx +3 -2
  80. package/src/common/image/NsNextImage.stories.tsx +2 -2
  81. package/src/common/image/NsNextImage.tsx +3 -7
  82. package/src/common/image/types.d.ts +6 -4
  83. package/src/common/index.ts +15 -8
  84. package/src/common/input/InputWrapper.tsx +3 -2
  85. package/src/common/input/NsInputField.spec.tsx +17 -2
  86. package/src/common/link/NsLink.spec.tsx +15 -8
  87. package/src/common/link/NsLink.stories.tsx +10 -2
  88. package/src/common/link/NsLink.styled.tsx +1 -1
  89. package/src/common/link/NsLink.tsx +26 -4
  90. package/src/common/link/types.d.ts +4 -2
  91. package/src/common/nav-product-card/NsNavProductCard.stories.tsx +2 -2
  92. package/src/common/nav-product-card/NsNavProductCard.tsx +3 -2
  93. package/src/common/nav-tab/NsNavTab.stories.tsx +2 -2
  94. package/src/common/typography/NsTypography.stories.tsx +2 -2
  95. package/src/common/videos/NsVideo.spec.tsx +26 -0
  96. package/src/common/videos/NsVideo.stories.tsx +32 -0
  97. package/src/common/videos/NsVideo.tsx +25 -0
  98. package/src/common/videos/index.ts +2 -0
  99. package/src/common/videos/types.d.ts +9 -0
  100. package/CHANGELOG.md +0 -194
  101. /package/src/common/breadcrumb/{types.ts → types.d.ts} +0 -0
@@ -0,0 +1,96 @@
1
+ import React from 'react';
2
+ import { render, screen, waitFor } from '@testing-library/react';
3
+ import { NsAutocomplete } from './';
4
+ import { OptionUiComponent } from './mock/MockUI';
5
+ import { AutocompleteMockList } from './mock/MockData';
6
+ import userEvent from '@testing-library/user-event';
7
+
8
+ describe('CustomAutocomplete', () => {
9
+ test('should render autocomplete component', () => {
10
+ render(
11
+ <NsAutocomplete
12
+ options={AutocompleteMockList}
13
+ onSelect={() => {}}
14
+ placeholder="autocomplete-input"
15
+ renderOptions={OptionUiComponent}
16
+ />
17
+ );
18
+ const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
19
+ expect(autocompleteInput).toBeInTheDocument();
20
+ });
21
+
22
+ test('displays options when typing in the input', async () => {
23
+ render(
24
+ <NsAutocomplete
25
+ options={AutocompleteMockList}
26
+ onSelect={() => {}}
27
+ renderOptions={OptionUiComponent}
28
+ placeholder="autocomplete-input"
29
+ />
30
+ );
31
+ const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
32
+ userEvent.type(autocompleteInput, 'GS Facial');
33
+ await waitFor(() => {
34
+ const option1 = screen.getByText('GS Facial Gels 2pk Subscription');
35
+ expect(option1).toBeInTheDocument();
36
+ });
37
+ });
38
+
39
+ test('selects an option when clicking on it', async () => {
40
+ render(
41
+ <NsAutocomplete
42
+ options={AutocompleteMockList}
43
+ renderOptions={OptionUiComponent}
44
+ placeholder="autocomplete-input"
45
+ />
46
+ );
47
+ const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
48
+ userEvent.type(autocompleteInput, 'GS Facial');
49
+ await waitFor(() => {
50
+ const optionLi = screen.getByText('GS Facial Spa');
51
+ userEvent.click(optionLi);
52
+ });
53
+ });
54
+
55
+ test('should load more options when clicking load more button', async () => {
56
+ render(
57
+ <NsAutocomplete
58
+ options={AutocompleteMockList}
59
+ renderOptions={OptionUiComponent}
60
+ placeholder="autocomplete-input"
61
+ seeMore={{
62
+ text: '',
63
+ show: true,
64
+ onSeeMore: () => ({}),
65
+ }}
66
+ />
67
+ );
68
+ const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
69
+ userEvent.type(autocompleteInput, 'GS Facial');
70
+ await waitFor(() => {
71
+ const loadMoreButton = screen.getByTestId('autocomplete-loadmore-id');
72
+ userEvent.click(loadMoreButton);
73
+ });
74
+ });
75
+
76
+ test('should clear options when clicking clear button', async () => {
77
+ render(
78
+ <NsAutocomplete
79
+ options={AutocompleteMockList}
80
+ renderOptions={OptionUiComponent}
81
+ placeholder="autocomplete-input"
82
+ clear={{
83
+ text: '',
84
+ show: true,
85
+ onClear: () => ({}),
86
+ }}
87
+ />
88
+ );
89
+ const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
90
+ userEvent.type(autocompleteInput, 'GS Facial');
91
+ await waitFor(() => {
92
+ const clearButton = screen.getByTestId('autocomplete-clear-id');
93
+ userEvent.click(clearButton);
94
+ });
95
+ });
96
+ });
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @author [Vijayaprathap Kumar] (vpkumar@nuskin.com)
3
+ * NsAutocomplete.stories.tsx
4
+ * @description NsAutocomplete component.
5
+ */
6
+
7
+ import React from 'react';
8
+ import type { StoryObj, Meta } from '@storybook/react';
9
+
10
+ import { withCenterDecorator } from '../../stories/decorators/withCenterDecorator';
11
+ import { NsAutocomplete } from './';
12
+ import { AutocompleteMockList } from './mock/MockData';
13
+ import { OptionUiComponent } from './mock/MockUI';
14
+ import { NsAutocompleteProps } from './types';
15
+
16
+ type ComponentType = typeof NsAutocomplete;
17
+ type Story = StoryObj<ComponentType>;
18
+ type MetaStory = Meta<ComponentType>;
19
+
20
+ export default {
21
+ title: 'Components/NsAutocomplete',
22
+ component: NsAutocomplete,
23
+ decorators: [withCenterDecorator],
24
+ argTypes: {
25
+ children: {
26
+ control: 'text',
27
+ },
28
+ },
29
+ args: {
30
+ options: AutocompleteMockList,
31
+ seeMore: {
32
+ text: 'See More Results',
33
+ show: false,
34
+ onSeeMore: () => {
35
+ throw new Error('Function not implemented.');
36
+ },
37
+ },
38
+ clear: {
39
+ text: 'Clear Recent Searches',
40
+ show: false,
41
+ onClear: () => {
42
+ throw new Error('Function not implemented.');
43
+ },
44
+ },
45
+ placeholder: 'search',
46
+ },
47
+ } as MetaStory;
48
+
49
+ export const Autocomplete: Story = {
50
+ render: (args: NsAutocompleteProps) => <NsAutocomplete {...args} renderOptions={OptionUiComponent} />,
51
+ };
@@ -0,0 +1,110 @@
1
+ import * as React from 'react';
2
+ import { NsInputField } from '../input';
3
+ import { Autocomplete, AutocompleteRenderGroupParams, List, AutocompleteRenderInputParams } from '@mui/material';
4
+ import { useState, PropsWithChildren, useRef } from 'react';
5
+ import { NsTypography } from '../typography';
6
+ import { NsAutocompleteProps, OptionsProps, ComponentboxProps } from './types';
7
+ import { NsButton } from '../button';
8
+
9
+ const NsAutocomplete = ({
10
+ id = 'custom_autocomplete_dropdown',
11
+ options = [],
12
+ renderOptions,
13
+ onSelect,
14
+ clear,
15
+ seeMore,
16
+ placeholder,
17
+ className,
18
+ }: PropsWithChildren<NsAutocompleteProps>) => {
19
+ const inputRef = useRef(null);
20
+
21
+ const [selectedOption, setSelectedOption] = useState<OptionsProps | null>(null);
22
+ const OptionUI = renderOptions;
23
+
24
+ const handleSelect = (event: React.SyntheticEvent<Element, Event>, value: OptionsProps | null) => {
25
+ setSelectedOption(value);
26
+ onSelect ? onSelect(value) : null;
27
+ };
28
+
29
+ const renderInput = (params: AutocompleteRenderInputParams) => (
30
+ <NsInputField {...params} placeholder={placeholder} inputRef={inputRef} />
31
+ );
32
+
33
+ const renderOption = (props: ComponentboxProps, option: OptionsProps) => (
34
+ <li {...props}>
35
+ <OptionUI {...props} option={option} />
36
+ </li>
37
+ );
38
+
39
+ const renderOptionLabel = (option: OptionsProps) => option.value;
40
+
41
+ const renderGroupBy = (option: OptionsProps) => option.group as string;
42
+
43
+ const renderGroupName = ({ key, group, children }: AutocompleteRenderGroupParams) => (
44
+ <li key={key}>
45
+ {group ? (
46
+ <NsTypography variant="p-s" className="p-3">
47
+ {group}
48
+ </NsTypography>
49
+ ) : null}
50
+ <span>{children}</span>
51
+ </li>
52
+ );
53
+
54
+ const renderClearButton = () => {
55
+ const { show, onClear, text = 'See More Results' } = clear || {};
56
+ if (!show) {
57
+ return null;
58
+ }
59
+ return (
60
+ <div className="text-right p-3">
61
+ <NsButton onClick={onClear} data-testid="autocomplete-clear-id">
62
+ <NsTypography variant={'p-xs'}>{text}</NsTypography>
63
+ </NsButton>
64
+ </div>
65
+ );
66
+ };
67
+
68
+ const renderSeeMoreButton = () => {
69
+ const { show, onSeeMore, text = 'See More Results' } = seeMore || {};
70
+ if (!show) {
71
+ return null;
72
+ }
73
+ return (
74
+ <div className="text-center p-3">
75
+ <NsButton onClick={onSeeMore} data-testid="autocomplete-loadmore-id">
76
+ <NsTypography variant="p-xs">{text}</NsTypography>
77
+ </NsButton>
78
+ </div>
79
+ );
80
+ };
81
+
82
+ const renderListBoxComponent = ({ children, ...otherProps }: ComponentboxProps) => (
83
+ <>
84
+ {renderClearButton()}
85
+ <List {...otherProps}>
86
+ {children}
87
+ {renderSeeMoreButton()}
88
+ </List>
89
+ </>
90
+ );
91
+
92
+ return (
93
+ <Autocomplete
94
+ id={id}
95
+ disablePortal
96
+ className={className}
97
+ options={options}
98
+ groupBy={renderGroupBy}
99
+ getOptionLabel={renderOptionLabel}
100
+ renderOption={renderOption}
101
+ renderInput={renderInput}
102
+ ListboxComponent={renderListBoxComponent}
103
+ renderGroup={renderGroupName}
104
+ onChange={handleSelect}
105
+ value={selectedOption}
106
+ />
107
+ );
108
+ };
109
+
110
+ export default NsAutocomplete;
@@ -0,0 +1 @@
1
+ export { default as NsAutocomplete } from './NsAutocomplete';
@@ -0,0 +1,23 @@
1
+ import { styled } from '@nuskin/foundation-theme';
2
+
3
+ export const ListStyled = styled.div`
4
+ display: flex;
5
+ width: 100%;
6
+ justify-content: space-between;
7
+ flex-direction: row;
8
+ align-items: center;
9
+ .item-description {
10
+ text-decoration: underline;
11
+ }
12
+ .add-to-cart-button {
13
+ margin-right: 5px;
14
+ height: 70%;
15
+ width: 40px;
16
+ border-left: 1px solid rgb(236, 237, 237);
17
+ }
18
+ img {
19
+ width: 50px;
20
+ height: 30px;
21
+ vertical-align: middle;
22
+ }
23
+ `;
@@ -0,0 +1,46 @@
1
+ import { OptionsProps } from '../types';
2
+
3
+ export const AutocompleteMockList: OptionsProps[] = [
4
+ {
5
+ id: '1',
6
+ value: 'GS Facial Gels 2pk Subscription',
7
+ // eslint-disable-next-line max-len
8
+ image: 'https://www.nuskin.com/content/products/86/99/01/86990168/_jcr_content/fullImage.img.160.160.png.64.64.png',
9
+ },
10
+ {
11
+ id: '2',
12
+ value: 'GS Facial',
13
+ // eslint-disable-next-line max-len
14
+ image: 'https://www.nuskin.com/content/products/01/55/00/01550014/_jcr_content/fullImage.img.160.160.png.64.64.png',
15
+ },
16
+ {
17
+ id: '3',
18
+ value: 'GS Facial Spa', // eslint-disable-next-line max-len
19
+ image: 'https://www.nuskin.com/content/products/01/10/12/01101251/_jcr_content/fullImage.img.160.160.png.64.64.png',
20
+ },
21
+ {
22
+ id: '4',
23
+ value: 'Nu Skin® Facial Spa',
24
+ group: 'Popular Searches', // eslint-disable-next-line max-len
25
+ image: 'https://www.nuskin.com/content/products/01/55/00/01550013/_jcr_content/fullImage.img.160.160.png.64.64.png',
26
+ icon: 'cart',
27
+ groupBgColor: '#000',
28
+ },
29
+ {
30
+ id: '5',
31
+ value: 'AP 24® Whitening Fluoride Toothpaste',
32
+ group: 'Popular Searches', // eslint-disable-next-line max-len
33
+ image: 'https://www.nuskin.com/content/products/86/99/01/86990100/_jcr_content/fullImage.img.160.160.png.64.64.png',
34
+ icon: 'cart',
35
+ groupBgColor: '#000',
36
+ },
37
+ {
38
+ id: '6',
39
+ value: 'PowerMask + LumiSpa Normal Subscription',
40
+ group: 'Popular Searches', // eslint-disable-next-line max-len
41
+ image: 'https://www.nuskin.com/content/products/01/00/16/01001647/jcr:content/fullImage.img.160.160.png/1611763135883-cache.png.64.64.png',
42
+ icon: 'cart',
43
+ groupBgColor: '#000',
44
+ },
45
+ ];
46
+
@@ -0,0 +1,24 @@
1
+ import { ListStyled } from './Mock.styled';
2
+ import { NsTypography } from '../../typography';
3
+ import { NsIcon } from '../../icon';
4
+ import { ListUiProps } from '../types';
5
+
6
+ const OptionUiComponent = ({ option }: ListUiProps) => {
7
+ return (
8
+ <ListStyled className="checker">
9
+ <div className="item-description" role="link" tabIndex={0}>
10
+ <NsTypography aria-label="Nu Skin Tumbler" variant="p-m" colorOverride="#63666A">
11
+ <img src={option.image} alt="No-image" />
12
+ {option?.value}
13
+ </NsTypography>
14
+ </div>
15
+ {option.icon ? (
16
+ <div className="add-to-cart-button" role="button" tabIndex={0} aria-label="add to cart">
17
+ <NsIcon name={option.icon} size="small" />
18
+ </div>
19
+ ) : null}
20
+ </ListStyled>
21
+ );
22
+ };
23
+
24
+ export { OptionUiComponent };
@@ -0,0 +1,39 @@
1
+ import React, { HTMLAttributes } from 'react';
2
+ import { AdditionalProps } from '../common.types';
3
+ import { NsIconNameType } from '../icon';
4
+ import { ComponentsProps } from '@mui/material';
5
+
6
+ export type NsAutocompleteProps = {
7
+ id?: string;
8
+ options: OptionsProps[];
9
+ renderOptions: React.ComponentType<any>;
10
+ // eslint-disable-next-line no-unused-vars
11
+ onSelect?: (params: OptionsProps | null) => void;
12
+ clear?: {
13
+ text: string;
14
+ show?: boolean; //default to false
15
+ onClear: () => {};
16
+ };
17
+ seeMore?: {
18
+ text: string;
19
+ show?: boolean; //default to false
20
+ onSeeMore: () => {};
21
+ };
22
+ placeholder?: string;
23
+ className?: string;
24
+ } & AdditionalProps;
25
+
26
+ export type ListUiProps = {
27
+ option: OptionsProps;
28
+ };
29
+
30
+ export type OptionsProps = {
31
+ id: string;
32
+ group?: string;
33
+ value: string;
34
+ icon?: NsIconNameType;
35
+ image?: string;
36
+ groupBgColor?: string;
37
+ };
38
+
39
+ export type ComponentboxProps = ComponentsProps & HTMLAttributes<HTMLElement>;
@@ -5,9 +5,9 @@ import { NsBanner } from './';
5
5
  describe('Hero component', () => {
6
6
  it('renders banner image and title', () => {
7
7
  const childText = 'Welcome to My Website';
8
- const image = 'https://example.com/banner.jpg';
8
+ const imageURL = 'https://example.com/banner.jpg';
9
9
  const { getByAltText, getByText } = render(
10
- <NsBanner imageUrl={image} altText="Banner image">
10
+ <NsBanner imageUrl={imageURL} altText="Banner image">
11
11
  <h1>{childText}</h1>
12
12
  </NsBanner>
13
13
  );
@@ -7,7 +7,7 @@
7
7
  import React from 'react';
8
8
  import type { StoryObj, Meta } from '@storybook/react';
9
9
  import { NsBanner, NsTypography, NsButton } from '../';
10
- import { CenterStoryDecorator } from '../../stories/CenterStoryDecorator';
10
+ import { withCenterDecorator } from '../../stories/decorators/withCenterDecorator';
11
11
 
12
12
  type ComponentType = typeof NsBanner;
13
13
  type Story = StoryObj<ComponentType>;
@@ -16,7 +16,7 @@ type MetaStory = Meta<ComponentType>;
16
16
  export default {
17
17
  title: 'Components/NsBanner',
18
18
  component: NsBanner,
19
- decorators: [CenterStoryDecorator],
19
+ decorators: [withCenterDecorator],
20
20
  argTypes: {
21
21
  children: {
22
22
  control: 'text', // use the text control to edit children prop
@@ -8,6 +8,7 @@ export const StyledBanner = styled.section`
8
8
  img {
9
9
  width: 100%;
10
10
  object-fit: cover;
11
+ height: auto;
11
12
  }
12
13
  `;
13
14
 
@@ -8,12 +8,14 @@ import React, { PropsWithChildren } from 'react';
8
8
 
9
9
  import { NsBannerProps } from './types';
10
10
  import { StyledBanner, StyledHeroText } from './NsBanner.styled';
11
+ import { NsImage } from '../';
11
12
 
13
+ /* TODO: once we have storybook 7, we can try converting this NsImage to NsNextImage */
12
14
  const NsBanner = (props: PropsWithChildren<NsBannerProps>) => {
13
15
  const {
14
16
  children,
15
- imageUrl = '',
16
- altText = 'NsBanner',
17
+ imageUrl,
18
+ altText,
17
19
  alignment = 'center',
18
20
  justify = 'start',
19
21
  className = '',
@@ -22,7 +24,14 @@ const NsBanner = (props: PropsWithChildren<NsBannerProps>) => {
22
24
 
23
25
  return (
24
26
  <StyledBanner className={className}>
25
- <img src={imageUrl} alt={altText} id="bgImage" {...additionalProps} />
27
+ <NsImage
28
+ src={imageUrl}
29
+ alt={altText || 'NsBanner'}
30
+ id="bgImage"
31
+ width={0}
32
+ height={0}
33
+ {...additionalProps}
34
+ />
26
35
  <StyledHeroText $alignment={alignment} $justify={justify}>
27
36
  <div className="mx-14">{children}</div>
28
37
  </StyledHeroText>
@@ -1,6 +1,6 @@
1
1
  import type { StoryObj, Meta } from '@storybook/react';
2
2
  import { NsBrandComponent } from './';
3
- import { CenterStoryDecorator } from '../../stories/CenterStoryDecorator';
3
+ import { withCenterDecorator } from '../../stories/decorators/withCenterDecorator';
4
4
 
5
5
  type ComponentType = typeof NsBrandComponent;
6
6
  type Story = StoryObj<ComponentType>;
@@ -9,7 +9,7 @@ type MetaStory = Meta<ComponentType>;
9
9
  export default {
10
10
  title: 'Components/NsBrandComponent',
11
11
  component: NsBrandComponent,
12
- decorators: [CenterStoryDecorator],
12
+ decorators: [withCenterDecorator],
13
13
  argTypes: {
14
14
  size: { control: 'text', defaultValue: '100px' },
15
15
  variant: {
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
- import { render } from '@testing-library/react';
2
+ import { render, screen } from '@testing-library/react';
3
3
  import { NsBreadcrumb } from './';
4
4
  import { ItemProps } from './types';
5
-
6
- const breadcrumbItems: ItemProps[] = [
5
+ const { getByText } = screen;
6
+ export const breadcrumbItems: ItemProps[] = [
7
7
  { label: 'Home', href: '/', icon: 'home' },
8
8
  { label: 'Products', href: '/products' },
9
9
  { label: 'Shirts', href: '/products/shirts' },
@@ -11,8 +11,16 @@ const breadcrumbItems: ItemProps[] = [
11
11
  ];
12
12
 
13
13
  describe('Breadcrumb', () => {
14
+ it('renders breadcrumb items', () => {
15
+ render(<NsBreadcrumb items={breadcrumbItems} />);
16
+ expect(getByText('Home')).toBeInTheDocument();
17
+ expect(getByText('Products')).toBeInTheDocument();
18
+ expect(getByText('Shirts')).toBeInTheDocument();
19
+ expect(getByText('Blue shirt')).toBeInTheDocument();
20
+ });
21
+
14
22
  it('renders breadcrumb items with navigation icons', () => {
15
- const { getByText } = render(<NsBreadcrumb items={breadcrumbItems} />);
23
+ render(<NsBreadcrumb items={breadcrumbItems} iconVisibility />);
16
24
  expect(getByText('Home')).toBeInTheDocument();
17
25
  expect(getByText('Products')).toBeInTheDocument();
18
26
  expect(getByText('Shirts')).toBeInTheDocument();
@@ -4,15 +4,20 @@
4
4
  * @description NsBanner component.
5
5
  */
6
6
 
7
- import React from 'react';
8
- import { ComponentStory, ComponentMeta } from '@storybook/react';
7
+ import type { Meta, StoryObj } from '@storybook/react';
9
8
 
10
- import Breadcrumb from './NsBreadcrumb';
9
+ import { NsBreadcrumb } from './';
10
+ import { withCenterDecorator } from '../../stories/decorators/withCenterDecorator';
11
+ import { breadcrumbItems } from './NsBreadcrumb.spec';
12
+
13
+ type ComponentType = typeof NsBreadcrumb;
14
+ type Story = StoryObj<ComponentType>;
15
+ type MetaStory = Meta<ComponentType>;
11
16
 
12
17
  export default {
13
- title: 'Components',
14
- component: Breadcrumb,
15
- decorators: [(story) => <div style={{ textAlign: 'center', marginTop: '20px' }}>{story()}</div>],
18
+ title: 'Components/NsBreadcrumb',
19
+ component: NsBreadcrumb,
20
+ decorators: [withCenterDecorator],
16
21
  argTypes: {
17
22
  iconVisibility: { control: 'boolean' },
18
23
  },
@@ -25,6 +30,12 @@ export default {
25
30
  ],
26
31
  iconVisibility: false,
27
32
  },
28
- } as ComponentMeta<typeof Breadcrumb>;
33
+ } as MetaStory;
29
34
 
30
- export const NsBreadcrumb: ComponentStory<typeof Breadcrumb> = (args) => <Breadcrumb {...args} />;
35
+ export const Breadcrumb: Story = {};
36
+ export const WithIcons: Story = {
37
+ args: {
38
+ items: breadcrumbItems,
39
+ iconVisibility: true,
40
+ },
41
+ };
@@ -15,7 +15,7 @@ export const BreadcrumbList = styled.ol`
15
15
  export const BreadcrumbItem = styled.li`
16
16
  display: flex;
17
17
  align-items: center;
18
- :not(:first-child) {
18
+ :not(:first-of-type) {
19
19
  margin-left: 12px;
20
20
  }
21
21
  & a {
@@ -7,11 +7,9 @@
7
7
  import { PropsWithChildren } from 'react';
8
8
 
9
9
  import { BreadcrumbContainer, BreadcrumbItem, BreadcrumbList } from './NsBreadcrumb.styled';
10
- import { NsIcon } from '../icon';
11
- import NsLink from '../link/NsLink';
12
- import { NsTypography } from '../typography';
10
+ import { NsTypography, NsLink, NsIcon } from '../';
13
11
  import { NsBreadcrumbProps, ItemProps } from './types';
14
- import { useTheme } from '@emotion/react';
12
+ import { useTheme } from '@mui/material/styles';
15
13
  import { ColorUtils } from '@nuskin/foundation-theme';
16
14
 
17
15
  const NsBreadcrumb = (props: PropsWithChildren<NsBreadcrumbProps>) => {
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import type { StoryObj, Meta } from '@storybook/react';
3
3
  import { NsButton } from './';
4
4
  import { ShoppingCart } from '@mui/icons-material';
5
- import { CenterStoryDecorator } from '../../stories/CenterStoryDecorator';
5
+ import { withCenterDecorator } from '../../stories/decorators/withCenterDecorator';
6
6
 
7
7
  type ComponentType = typeof NsButton;
8
8
  type Story = StoryObj<ComponentType>;
@@ -11,7 +11,7 @@ type MetaStory = Meta<ComponentType>;
11
11
  export default {
12
12
  title: 'Components/NsButton',
13
13
  component: NsButton,
14
- decorators: [CenterStoryDecorator],
14
+ decorators: [withCenterDecorator],
15
15
  argTypes: {
16
16
  children: {
17
17
  control: 'text',
@@ -0,0 +1,18 @@
1
+ import { NsNextImage, NsLink } from '../';
2
+ import { NsFfgStyled } from './styled/NsFfgStyled';
3
+ import { NsFfgCardProps } from './types';
4
+
5
+ const NsFfgCard = ({ actionText, actionLink, text, topImageUrl, topImageAlt, additionalProps }: NsFfgCardProps) => {
6
+ return (
7
+ <NsFfgStyled data-testid={additionalProps?.testid}>
8
+ <div className="top-image">
9
+ <NsNextImage height={192} width={288} alt={topImageAlt} src={topImageUrl} />
10
+ </div>
11
+ <div className="body-text">{text}</div>
12
+ <NsLink className="action-link" href={actionLink}>
13
+ {actionText || 'LEARN MORE'}
14
+ </NsLink>
15
+ </NsFfgStyled>
16
+ );
17
+ };
18
+ export default NsFfgCard;
@@ -1,4 +1,5 @@
1
1
  export { default as NsBrandWrapper } from './NsBrandWrapper';
2
2
  export { default as NsBgCard } from './NsBgCard';
3
+ export { default as NsFfgCard } from './NsFfgCard';
3
4
  export { default as NsBrandLogo } from './NsBrandLogo';
4
5
  export { default as NsBrandProductCard } from './NsBrandProductCard';
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { NsBgCard } from '../';
3
- import { CenterStoryDecorator } from '../../../stories/CenterStoryDecorator';
3
+ import { withCenterDecorator } from '../../../stories/decorators/withCenterDecorator';
4
4
 
5
5
  type ComponentType = typeof NsBgCard;
6
6
  type Story = StoryObj<ComponentType>;
@@ -16,7 +16,7 @@ export default {
16
16
  // brandImageUrl:
17
17
  // 'https://media.nuskin.com/m/2571a0407433a6e8/webimage-brand-lumispa.png',
18
18
  },
19
- decorators: [CenterStoryDecorator],
19
+ decorators: [withCenterDecorator],
20
20
  } as MetaStory;
21
21
 
22
22
  export const BgCard: Story = {};
@@ -1,5 +1,5 @@
1
1
  import type { StoryObj, Meta } from '@storybook/react';
2
- import { CenterStoryDecorator } from '../../../stories/CenterStoryDecorator';
2
+ import { withCenterDecorator } from '../../../stories/decorators/withCenterDecorator';
3
3
  import { NsBrandProductCard } from '../';
4
4
 
5
5
  type ComponentType = typeof NsBrandProductCard;
@@ -10,7 +10,7 @@ export default {
10
10
  component: NsBrandProductCard,
11
11
  title: 'Components/NsCard/NsBrandProductCard',
12
12
  argTypes: {},
13
- decorators: [CenterStoryDecorator],
13
+ decorators: [withCenterDecorator],
14
14
  } as MetaStory;
15
15
 
16
16
  export const Default: Story = {