@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.
- package/package.json +21 -16
- package/src/common/alert/NsAlert.spec.tsx +134 -0
- package/src/common/alert/NsAlert.stories.tsx +42 -0
- package/src/common/alert/NsAlert.styled.ts +124 -0
- package/src/common/alert/NsAlert.tsx +155 -0
- package/src/common/alert/index.ts +2 -0
- package/src/common/alert/types.d.ts +45 -0
- package/src/common/auth-dropdown/NsAuthDropdown.stories.tsx +9 -5
- package/src/common/auth-dropdown/NsAuthDropdown.tsx +1 -1
- package/src/common/autocomplete/NsAutocomplete.spec.tsx +96 -0
- package/src/common/autocomplete/NsAutocomplete.stories.tsx +51 -0
- package/src/common/autocomplete/NsAutocomplete.tsx +110 -0
- package/src/common/autocomplete/index.ts +1 -0
- package/src/common/autocomplete/mock/Mock.styled.tsx +23 -0
- package/src/common/autocomplete/mock/MockData.ts +46 -0
- package/src/common/autocomplete/mock/MockUI.tsx +24 -0
- package/src/common/autocomplete/types.ts +39 -0
- package/src/common/banner/NsBanner.spec.tsx +2 -2
- package/src/common/banner/NsBanner.stories.tsx +2 -2
- package/src/common/banner/NsBanner.styled.tsx +1 -0
- package/src/common/banner/NsBanner.tsx +12 -3
- package/src/common/brand-components/NsBrand.stories.tsx +2 -2
- package/src/common/breadcrumb/NsBreadcrumb.spec.tsx +12 -4
- package/src/common/breadcrumb/NsBreadcrumb.stories.tsx +19 -8
- package/src/common/breadcrumb/NsBreadcrumb.styled.tsx +1 -1
- package/src/common/breadcrumb/NsBreadcrumb.tsx +2 -4
- package/src/common/button/NsButton.stories.tsx +2 -2
- package/src/common/card/NsFfgCard.tsx +18 -0
- package/src/common/card/index.ts +1 -0
- package/src/common/card/stories/NsBgCard.stories.tsx +2 -2
- package/src/common/card/stories/NsBrandProductCard.stories.tsx +2 -2
- package/src/common/card/stories/NsFfgCard.stories.tsx +40 -0
- package/src/common/card/styled/NsFfgStyled.tsx +44 -0
- package/src/common/card/tests/NsFfgCard.spec.tsx +23 -0
- package/src/common/card/types.d.ts +9 -0
- package/src/common/carousal/CarousalControl.tsx +3 -5
- package/src/common/carousal/IconWrapper.tsx +4 -4
- package/src/common/carousal/tests/NsCarousal.spec.tsx +4 -4
- package/src/common/cart-icon/NsCartIcon.spec.tsx +10 -4
- package/src/common/cart-icon/NsCartIcon.stories.tsx +2 -2
- package/src/common/checkbox/NsCheckbox.stories.tsx +12 -16
- package/src/common/column-control/NDivs.story.tsx +35 -0
- package/src/common/column-control/NsColumnControl.spec.tsx +24 -0
- package/src/common/column-control/NsColumnControl.stories.tsx +69 -0
- package/src/common/column-control/NsColumnControl.tsx +59 -0
- package/src/common/column-control/index.ts +2 -0
- package/src/common/column-control/types.d.ts +47 -0
- package/src/common/column-control/utils.ts +66 -0
- package/src/common/dropdown/NsDropdown.tsx +1 -1
- package/src/common/dropdown/stories/NsDropdown.stories.tsx +13 -9
- package/src/common/dropdown/stories/NsDropdownCountry.stories.tsx +13 -9
- package/src/common/flag/NsFlag.stories.tsx +2 -2
- package/src/common/icon/NsIcon.spec.tsx +9 -2
- package/src/common/icon/NsIcon.stories.tsx +4 -17
- package/src/common/icon/NsIcon.tsx +1 -1
- package/src/common/icon/{IconMap.ts → NsIconVariants.ts} +10 -0
- package/src/common/icon/icons/AlertErrorIcon.tsx +36 -0
- package/src/common/icon/icons/ArrowDown.tsx +1 -1
- package/src/common/{carousal/LeftChevron.tsx → icon/icons/ArrowLeft.tsx} +7 -5
- package/src/common/{carousal/RightChevron.tsx → icon/icons/ArrowRight.tsx} +3 -3
- package/src/common/icon/icons/ArrowUp.tsx +1 -1
- package/src/common/icon/icons/Cart.tsx +7 -1
- package/src/common/icon/icons/CartBadge.tsx +7 -1
- package/src/common/icon/icons/CheckShield.tsx +7 -1
- package/src/common/icon/icons/CheckVerified.tsx +7 -1
- package/src/common/icon/icons/CloseIcon.tsx +22 -0
- package/src/common/icon/icons/ErrorIcon.tsx +1 -1
- package/src/common/icon/icons/InfoFilled.tsx +1 -1
- package/src/common/icon/icons/InfoOutlined.tsx +7 -1
- package/src/common/icon/icons/SearchIcon.tsx +1 -1
- package/src/common/icon/icons/SlashIcon.tsx +4 -4
- package/src/common/icon/icons/SuccessIcon.tsx +1 -1
- package/src/common/icon/icons/TrashIcon.tsx +1 -2
- package/src/common/icon/icons/WarningIcon.tsx +36 -0
- package/src/common/icon/icons/home.tsx +4 -3
- package/src/common/icon/index.ts +1 -0
- package/src/common/icon/types.d.ts +2 -15
- package/src/common/image/NsImage.stories.tsx +2 -2
- package/src/common/image/NsImage.tsx +3 -2
- package/src/common/image/NsNextImage.stories.tsx +2 -2
- package/src/common/image/NsNextImage.tsx +3 -7
- package/src/common/image/types.d.ts +6 -4
- package/src/common/index.ts +15 -8
- package/src/common/input/InputWrapper.tsx +3 -2
- package/src/common/input/NsInputField.spec.tsx +17 -2
- package/src/common/link/NsLink.spec.tsx +15 -8
- package/src/common/link/NsLink.stories.tsx +10 -2
- package/src/common/link/NsLink.styled.tsx +1 -1
- package/src/common/link/NsLink.tsx +26 -4
- package/src/common/link/types.d.ts +4 -2
- package/src/common/nav-product-card/NsNavProductCard.stories.tsx +2 -2
- package/src/common/nav-product-card/NsNavProductCard.tsx +3 -2
- package/src/common/nav-tab/NsNavTab.stories.tsx +2 -2
- package/src/common/typography/NsTypography.stories.tsx +2 -2
- package/src/common/videos/NsVideo.spec.tsx +26 -0
- package/src/common/videos/NsVideo.stories.tsx +32 -0
- package/src/common/videos/NsVideo.tsx +25 -0
- package/src/common/videos/index.ts +2 -0
- package/src/common/videos/types.d.ts +9 -0
- package/CHANGELOG.md +0 -194
- /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
|
|
8
|
+
const imageURL = 'https://example.com/banner.jpg';
|
|
9
9
|
const { getByAltText, getByText } = render(
|
|
10
|
-
<NsBanner imageUrl={
|
|
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 {
|
|
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: [
|
|
19
|
+
decorators: [withCenterDecorator],
|
|
20
20
|
argTypes: {
|
|
21
21
|
children: {
|
|
22
22
|
control: 'text', // use the text control to edit children prop
|
|
@@ -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
|
|
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
|
-
<
|
|
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 {
|
|
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: [
|
|
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
|
-
|
|
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
|
|
8
|
-
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
|
7
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
9
8
|
|
|
10
|
-
import
|
|
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:
|
|
15
|
-
decorators: [
|
|
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
|
|
33
|
+
} as MetaStory;
|
|
29
34
|
|
|
30
|
-
export const
|
|
35
|
+
export const Breadcrumb: Story = {};
|
|
36
|
+
export const WithIcons: Story = {
|
|
37
|
+
args: {
|
|
38
|
+
items: breadcrumbItems,
|
|
39
|
+
iconVisibility: true,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -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 '../
|
|
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 '@
|
|
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 {
|
|
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: [
|
|
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;
|
package/src/common/card/index.ts
CHANGED
|
@@ -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 {
|
|
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: [
|
|
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 {
|
|
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: [
|
|
13
|
+
decorators: [withCenterDecorator],
|
|
14
14
|
} as MetaStory;
|
|
15
15
|
|
|
16
16
|
export const Default: Story = {
|