@nuskin/foundation-ui-components 1.6.0 → 1.7.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 -1
- package/src/common/autocomplete/NsAutocomplete.styled.tsx +5 -1
- package/src/common/autocomplete/NsAutocomplete.tsx +19 -71
- package/src/common/autocomplete/helper/ClearButton.tsx +22 -0
- package/src/common/autocomplete/helper/SeeMoreButton.tsx +47 -0
- package/src/common/autocomplete/helper/renderGroupName.tsx +17 -0
- package/src/common/autocomplete/stories/ACWithoutClose.tsx +49 -0
- package/src/common/autocomplete/stories/NsAutocomplete.stories.tsx +76 -0
- package/src/common/autocomplete/stories/RenderAutoComplete.tsx +60 -0
- package/src/common/autocomplete/types.d.ts +1 -1
- package/src/common/card/NsBrandCard.tsx +94 -0
- package/src/common/card/NsCheckoutCard.tsx +92 -0
- package/src/common/card/NsHeroCardLevel1.tsx +106 -0
- package/src/common/card/NsHeroCardLevel2.tsx +126 -0
- package/src/common/card/NsIconText.tsx +153 -0
- package/src/common/card/NsLargeCard.tsx +120 -0
- package/src/common/card/NsMediumCard.tsx +64 -0
- package/src/common/card/NsPost.tsx +60 -0
- package/src/common/card/NsSmallFullCard.tsx +90 -0
- package/src/common/card/NsXLargeCard.tsx +80 -0
- package/src/common/card/cardSizes.ts +12 -0
- package/src/common/card/index.ts +10 -2
- package/src/common/card/specs/BrandLogo.spec.tsx +1 -1
- package/src/common/card/specs/NsBgCard.spec.tsx +2 -2
- package/src/common/card/specs/NsBrandCard.spec.tsx +97 -0
- package/src/common/card/specs/NsCheckoutCard.spec.tsx +50 -0
- package/src/common/card/specs/NsHeroCardLevel1.spec.tsx +48 -0
- package/src/common/card/specs/NsHeroCardLevel2.spec.tsx +51 -0
- package/src/common/card/specs/NsIconText.spec.tsx +28 -0
- package/src/common/card/specs/NsLargeCard.spec.tsx +41 -0
- package/src/common/card/specs/NsMediumCard.spec.tsx +35 -0
- package/src/common/card/specs/NsPost.spec.tsx +31 -0
- package/src/common/card/specs/NsSmallFullCard.spec.tsx +36 -0
- package/src/common/card/specs/NsXLargeCard.spec.tsx +35 -0
- package/src/common/card/stories/{NsBrandProductCard.stories.tsx → NsBrandCard.stories.tsx} +9 -4
- package/src/common/card/stories/NsCheckoutCard.stories.tsx +24 -0
- package/src/common/card/stories/NsHeroCardLevel1.stories.tsx +45 -0
- package/src/common/card/stories/NsHeroCardLevel2.stories.tsx +50 -0
- package/src/common/card/stories/NsIconText.stories.tsx +36 -0
- package/src/common/card/stories/NsLargeCard.stories.tsx +32 -0
- package/src/common/card/stories/NsMediumCard.stories.tsx +29 -0
- package/src/common/card/stories/NsPost.stories.tsx +39 -0
- package/src/common/card/stories/NsSmallFullCard.stories.tsx +29 -0
- package/src/common/card/stories/NsXLargeCard.stories.tsx +29 -0
- package/src/common/carousal/RenderBrand.tsx +3 -2
- package/src/common/input/InputWrapper.tsx +1 -1
- package/src/common/autocomplete/NsAutocomplete.stories.tsx +0 -108
- package/src/common/card/NsBrandProductCard.tsx +0 -47
- package/src/common/card/NsFfgCard.tsx +0 -24
- package/src/common/card/specs/NsBrandProductCard.spec.tsx +0 -32
- package/src/common/card/specs/NsFfgCard.spec.tsx +0 -33
- package/src/common/card/stories/NsBgCard.stories.tsx +0 -20
- package/src/common/card/stories/NsFfgCard.stories.tsx +0 -73
- package/src/common/card/styled/NsFfgStyled.tsx +0 -46
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { styled } from '@nuskin/foundation-theme';
|
|
2
|
-
|
|
3
2
|
import { NsInputField } from '../input';
|
|
4
3
|
|
|
4
|
+
// TODO: why is .muiautocomplete-popupindicator added?
|
|
5
5
|
export const StyledTextField = styled(NsInputField)`
|
|
6
6
|
.MuiAutocomplete-inputRoot {
|
|
7
7
|
padding-right: 10px !important;
|
|
@@ -9,4 +9,8 @@ export const StyledTextField = styled(NsInputField)`
|
|
|
9
9
|
.muiautocomplete-popupindicator {
|
|
10
10
|
display: none;
|
|
11
11
|
}
|
|
12
|
+
.styled-text-field.MuiOutlinedInput-root input#custom_autocomplete_dropdown::placeholder {
|
|
13
|
+
opacity: 1;
|
|
14
|
+
color: #252525;
|
|
15
|
+
}
|
|
12
16
|
`;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import { Autocomplete, AutocompleteRenderInputParams, List } from '@mui/material';
|
|
1
2
|
import React, { HTMLAttributes, PropsWithChildren, useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { Autocomplete, AutocompleteRenderGroupParams, List, AutocompleteRenderInputParams } from '@mui/material';
|
|
3
|
-
import { NsTypography, NsButton } from '../';
|
|
4
|
-
import { NsAutocompleteProps, OptionsProps, ComponentBoxProps } from './types.d';
|
|
5
|
-
import { StyledTextField } from './NsAutocomplete.styled';
|
|
6
|
-
|
|
7
|
-
const renderOptionLabel = (option: OptionsProps) => option.value;
|
|
8
3
|
|
|
9
|
-
|
|
4
|
+
import { ClearButton } from './helper/ClearButton';
|
|
5
|
+
import { renderGroupBy, renderGroupName, renderOptionLabel } from './helper/renderGroupName';
|
|
6
|
+
import { SeeMoreButton } from './helper/SeeMoreButton';
|
|
7
|
+
import { StyledTextField } from './NsAutocomplete.styled';
|
|
8
|
+
import { ComponentBoxProps, NsAutocompleteProps, OptionsProps } from './types.d';
|
|
10
9
|
|
|
11
10
|
const NsAutocomplete = ({
|
|
12
11
|
id = 'custom_autocomplete_dropdown',
|
|
@@ -30,7 +29,7 @@ const NsAutocomplete = ({
|
|
|
30
29
|
const listboxRef = useRef<HTMLUListElement | null>(null);
|
|
31
30
|
|
|
32
31
|
useEffect(() => {
|
|
33
|
-
if (listboxRef
|
|
32
|
+
if (listboxRef.current && options.length > 0) {
|
|
34
33
|
listboxRef.current.scrollTop = scrollTopPosition;
|
|
35
34
|
}
|
|
36
35
|
}, [options, scrollTopPosition]);
|
|
@@ -44,7 +43,7 @@ const NsAutocomplete = ({
|
|
|
44
43
|
[onSelect]
|
|
45
44
|
);
|
|
46
45
|
|
|
47
|
-
const
|
|
46
|
+
const onInputChange = useCallback(
|
|
48
47
|
(event: React.SyntheticEvent, newInputValue: string) => {
|
|
49
48
|
if (typeof onChange === 'function') {
|
|
50
49
|
onChange(newInputValue);
|
|
@@ -59,82 +58,31 @@ const NsAutocomplete = ({
|
|
|
59
58
|
placeholder={placeholder}
|
|
60
59
|
InputProps={{
|
|
61
60
|
...params.InputProps,
|
|
61
|
+
className: 'styled-text-field',
|
|
62
62
|
startAdornment: iconStart,
|
|
63
63
|
endAdornment: iconEnd,
|
|
64
64
|
}}
|
|
65
65
|
/>
|
|
66
66
|
);
|
|
67
67
|
|
|
68
|
-
// TODO: @vijayprathap need to check if we can remove object spread of ...props and ...params in renderInput
|
|
69
68
|
const renderOption = (props: ComponentBoxProps, option: OptionsProps) => (
|
|
70
69
|
<li {...props} key={props.id}>
|
|
71
70
|
{OptionsUI ? <OptionsUI {...props} option={option} /> : null}
|
|
72
71
|
</li>
|
|
73
72
|
);
|
|
74
73
|
|
|
75
|
-
const renderGroupName = ({ key, group, children }: AutocompleteRenderGroupParams) => (
|
|
76
|
-
<li key={key}>
|
|
77
|
-
{group ? (
|
|
78
|
-
<NsTypography variant="p-m" weight="bold" className="p-3">
|
|
79
|
-
{group}
|
|
80
|
-
</NsTypography>
|
|
81
|
-
) : null}
|
|
82
|
-
<ul className="p-0">{children}</ul>
|
|
83
|
-
</li>
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
const renderClearButton = useCallback(() => {
|
|
87
|
-
const { show, onClear, text = 'Clear Search' } = clear || {};
|
|
88
|
-
if (!show) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
return (
|
|
92
|
-
<li className="text-right p-3">
|
|
93
|
-
<NsButton onClick={onClear} data-testid="autocomplete-clear-id">
|
|
94
|
-
<NsTypography variant="p-xs" weight="bold">
|
|
95
|
-
{text}
|
|
96
|
-
</NsTypography>
|
|
97
|
-
</NsButton>
|
|
98
|
-
</li>
|
|
99
|
-
);
|
|
100
|
-
}, [clear]);
|
|
101
|
-
|
|
102
|
-
const handleSeeMore = useCallback(() => {
|
|
103
|
-
const { onSeeMore } = seeMore || {};
|
|
104
|
-
// Remember the current scroll position
|
|
105
|
-
if (options?.length > 0 && listboxRef?.current) {
|
|
106
|
-
const lastScrollPosition = listboxRef.current?.scrollTop;
|
|
107
|
-
if (lastScrollPosition) {
|
|
108
|
-
setScrollTopPosition(lastScrollPosition);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (onSeeMore && typeof onSeeMore === 'function') {
|
|
112
|
-
onSeeMore();
|
|
113
|
-
}
|
|
114
|
-
}, [seeMore, options]);
|
|
115
|
-
|
|
116
|
-
const renderSeeMoreButton = useCallback(() => {
|
|
117
|
-
const { show, text = 'See More Results' } = seeMore || {};
|
|
118
|
-
if (!show) {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return (
|
|
123
|
-
<li className="text-center p-3">
|
|
124
|
-
<NsButton onClick={handleSeeMore} data-testid="autocomplete-loadmore-id">
|
|
125
|
-
<NsTypography variant="p-xs" weight="bold">
|
|
126
|
-
{text}
|
|
127
|
-
</NsTypography>
|
|
128
|
-
</NsButton>
|
|
129
|
-
</li>
|
|
130
|
-
);
|
|
131
|
-
}, [seeMore, handleSeeMore]);
|
|
132
|
-
|
|
133
74
|
const renderListBoxComponent = ({ children, ...otherProps }: ComponentBoxProps) => (
|
|
134
75
|
<List {...otherProps} ref={listboxRef}>
|
|
135
|
-
{
|
|
76
|
+
<ClearButton show={clear?.show} text={clear?.text} onClear={clear?.onClear} />
|
|
136
77
|
{children}
|
|
137
|
-
|
|
78
|
+
<SeeMoreButton
|
|
79
|
+
onSeeMore={seeMore?.onSeeMore}
|
|
80
|
+
show={seeMore?.show}
|
|
81
|
+
text={seeMore?.text}
|
|
82
|
+
options={options}
|
|
83
|
+
setScrollTopPosition={setScrollTopPosition}
|
|
84
|
+
listboxRef={listboxRef}
|
|
85
|
+
/>
|
|
138
86
|
</List>
|
|
139
87
|
);
|
|
140
88
|
|
|
@@ -154,7 +102,7 @@ const NsAutocomplete = ({
|
|
|
154
102
|
ListboxProps={{ scrollTopPosition } as HTMLAttributes<HTMLUListElement>} // Pass the current scroll position
|
|
155
103
|
renderGroup={renderGroupName}
|
|
156
104
|
onChange={handleSelect}
|
|
157
|
-
onInputChange={
|
|
105
|
+
onInputChange={onInputChange}
|
|
158
106
|
value={value}
|
|
159
107
|
inputValue={inputValue}
|
|
160
108
|
filterOptions={
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { NsButton, NsTypography } from '../..';
|
|
2
|
+
|
|
3
|
+
type ClearButtonProps = {
|
|
4
|
+
text?: string;
|
|
5
|
+
show?: boolean;
|
|
6
|
+
onClear?(): void;
|
|
7
|
+
};
|
|
8
|
+
export const ClearButton = (props: ClearButtonProps) => {
|
|
9
|
+
const { show, onClear, text = 'Clear All' } = props ?? {};
|
|
10
|
+
if (!show) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return (
|
|
14
|
+
<li className="text-right p-3">
|
|
15
|
+
<NsButton onClick={onClear} data-testid="autocomplete-clear-id">
|
|
16
|
+
<NsTypography variant="p-xs" weight="bold">
|
|
17
|
+
{text}
|
|
18
|
+
</NsTypography>
|
|
19
|
+
</NsButton>
|
|
20
|
+
</li>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Dispatch, MutableRefObject, SetStateAction, useCallback } from 'react';
|
|
2
|
+
import { NsButton, NsTypography } from '../..';
|
|
3
|
+
|
|
4
|
+
type SeeMoreButtonProps = {
|
|
5
|
+
text?: string;
|
|
6
|
+
show?: boolean;
|
|
7
|
+
onSeeMore?(): void;
|
|
8
|
+
options: any[];
|
|
9
|
+
setScrollTopPosition: Dispatch<SetStateAction<number>>;
|
|
10
|
+
listboxRef: MutableRefObject<HTMLUListElement | null>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const SeeMoreButton = (props: SeeMoreButtonProps) => {
|
|
14
|
+
const {
|
|
15
|
+
show = false,
|
|
16
|
+
text = 'See More Results',
|
|
17
|
+
onSeeMore,
|
|
18
|
+
options,
|
|
19
|
+
listboxRef,
|
|
20
|
+
setScrollTopPosition,
|
|
21
|
+
} = props || {};
|
|
22
|
+
|
|
23
|
+
const handleSeeMore = useCallback(() => {
|
|
24
|
+
/* Remember the current scroll position */
|
|
25
|
+
if (options?.length > 0 && listboxRef?.current) {
|
|
26
|
+
const lastScrollPosition = listboxRef.current?.scrollTop;
|
|
27
|
+
if (lastScrollPosition) {
|
|
28
|
+
setScrollTopPosition(lastScrollPosition);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (onSeeMore && typeof onSeeMore === 'function') {
|
|
32
|
+
onSeeMore();
|
|
33
|
+
}
|
|
34
|
+
}, [listboxRef, onSeeMore, options?.length, setScrollTopPosition]);
|
|
35
|
+
if (!show) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return (
|
|
39
|
+
<li className="text-center p-3">
|
|
40
|
+
<NsButton onClick={handleSeeMore} data-testid="autocomplete-loadmore-id">
|
|
41
|
+
<NsTypography variant="p-xs" weight="bold">
|
|
42
|
+
{text}
|
|
43
|
+
</NsTypography>
|
|
44
|
+
</NsButton>
|
|
45
|
+
</li>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AutocompleteRenderGroupParams } from '@mui/material';
|
|
2
|
+
import { NsTypography, OptionsProps } from '../..';
|
|
3
|
+
|
|
4
|
+
export const renderGroupName = ({ key, group, children }: AutocompleteRenderGroupParams) => (
|
|
5
|
+
<li key={key}>
|
|
6
|
+
{group ? (
|
|
7
|
+
<NsTypography variant="p-m" weight="bold" className="p-3">
|
|
8
|
+
{group}
|
|
9
|
+
</NsTypography>
|
|
10
|
+
) : null}
|
|
11
|
+
<ul className="p-0">{children}</ul>
|
|
12
|
+
</li>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export const renderOptionLabel = (option: OptionsProps) => option.value;
|
|
16
|
+
|
|
17
|
+
export const renderGroupBy = (option: OptionsProps) => option.group as string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { NsAutocompleteProps, OptionsProps } from '../types';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { NsAutocomplete } from '..';
|
|
5
|
+
import { OptionUiComponent } from '../mock/MockUI';
|
|
6
|
+
|
|
7
|
+
const PAGE_SIZE = 6;
|
|
8
|
+
|
|
9
|
+
export const ACWithoutClose = (args: NsAutocompleteProps) => {
|
|
10
|
+
const [selectedOption, setSelectedOption] = useState<string | OptionsProps | null>(null);
|
|
11
|
+
const [searchTerms, setSearchTerms] = useState<string>('');
|
|
12
|
+
const [pageNo, setPageNo] = useState(PAGE_SIZE);
|
|
13
|
+
|
|
14
|
+
const onSeeMoreHandler = () => {
|
|
15
|
+
setPageNo(PAGE_SIZE + PAGE_SIZE);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const AutoCompleteProps = {
|
|
19
|
+
...args,
|
|
20
|
+
seeMore: {
|
|
21
|
+
...args.seeMore,
|
|
22
|
+
onSeeMore: onSeeMoreHandler,
|
|
23
|
+
},
|
|
24
|
+
} as NsAutocompleteProps;
|
|
25
|
+
|
|
26
|
+
const onSelect = (value: string | OptionsProps | null) => {
|
|
27
|
+
setSelectedOption(value);
|
|
28
|
+
setSearchTerms((value = ''));
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const handleInputChange = (newValue: string) => {
|
|
32
|
+
setSearchTerms(newValue);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div>
|
|
37
|
+
<NsAutocomplete
|
|
38
|
+
{...AutoCompleteProps}
|
|
39
|
+
options={args.options.slice(0, pageNo)}
|
|
40
|
+
onChange={handleInputChange}
|
|
41
|
+
onSelect={onSelect}
|
|
42
|
+
OptionsUI={OptionUiComponent}
|
|
43
|
+
value={selectedOption}
|
|
44
|
+
inputValue={searchTerms}
|
|
45
|
+
iconEnd={undefined}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { StoryObj, Meta } from '@storybook/react';
|
|
4
|
+
|
|
5
|
+
import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
|
|
6
|
+
import { NsAutocomplete } from '..';
|
|
7
|
+
import { AutocompleteMockList } from '../mock/MockData';
|
|
8
|
+
import { NsIcon } from '../../icon';
|
|
9
|
+
import { RenderAutoComplete } from './RenderAutoComplete';
|
|
10
|
+
import { ACWithoutClose } from './ACWithoutClose';
|
|
11
|
+
|
|
12
|
+
type ComponentType = typeof NsAutocomplete;
|
|
13
|
+
type Story = StoryObj<ComponentType>;
|
|
14
|
+
type MetaStory = Meta<ComponentType>;
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
title: 'Components/Inputs/NsAutocomplete',
|
|
18
|
+
component: NsAutocomplete,
|
|
19
|
+
decorators: [withCenterDecorator],
|
|
20
|
+
argTypes: {
|
|
21
|
+
children: {
|
|
22
|
+
control: 'text',
|
|
23
|
+
},
|
|
24
|
+
options: {
|
|
25
|
+
description: 'An array of objects representing the available options for the autocomplete.',
|
|
26
|
+
},
|
|
27
|
+
OptionsUI: {
|
|
28
|
+
description: '(optional): A custom component to render each option in the dropdown.',
|
|
29
|
+
},
|
|
30
|
+
loading: {
|
|
31
|
+
description: '(optional): A boolean indicating whether the autocomplete is in a loading state.',
|
|
32
|
+
},
|
|
33
|
+
noOptionsText: { description: '(optional): The text to display when there are no options available.' },
|
|
34
|
+
onSelect: { description: '(optional): A callback function to be called when an option is selected.' },
|
|
35
|
+
onChange: { description: '(optional): A callback function to be called when the input value changes.' },
|
|
36
|
+
clear: { description: '(optional): An object containing properties for the clear button.' },
|
|
37
|
+
seeMore: { description: '(optional): An object containing properties for the "see more" button.' },
|
|
38
|
+
placeholder: { description: '(optional): The placeholder text for the input field.' },
|
|
39
|
+
className: { description: '(optional): Additional CSS class name(s) for the autocomplete component.' },
|
|
40
|
+
iconStart: {
|
|
41
|
+
description: '(optional): An icon or React node to be displayed at the start of the input field.',
|
|
42
|
+
},
|
|
43
|
+
iconEnd: { description: '(optional): An icon or React node to be displayed at the end of the input field.' },
|
|
44
|
+
inputValue: { description: ' The current value of the input field.' },
|
|
45
|
+
value: { description: ': The currently selected option.' },
|
|
46
|
+
isFilterEnabled: { description: ' (optional): A boolean indicating whether filtering of options is enabled.' },
|
|
47
|
+
},
|
|
48
|
+
args: {
|
|
49
|
+
options: AutocompleteMockList,
|
|
50
|
+
seeMore: {
|
|
51
|
+
text: 'See More Results',
|
|
52
|
+
show: true,
|
|
53
|
+
onSeeMore: () => {
|
|
54
|
+
throw new Error('Function not implemented.');
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
clear: {
|
|
58
|
+
text: 'Clear Recent Searches',
|
|
59
|
+
show: true,
|
|
60
|
+
onClear: () => {
|
|
61
|
+
throw new Error('Function not implemented.');
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
iconStart: <NsIcon name="search" />,
|
|
65
|
+
iconEnd: <NsIcon name="roundedClose" size="small" colorOverride="#F5F5F5" />,
|
|
66
|
+
placeholder: 'search',
|
|
67
|
+
},
|
|
68
|
+
} as MetaStory;
|
|
69
|
+
|
|
70
|
+
export const Autocomplete: Story = {
|
|
71
|
+
render: RenderAutoComplete,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const WithoutClose: Story = {
|
|
75
|
+
render: ACWithoutClose,
|
|
76
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { NsAutocompleteProps, OptionsProps } from '../types';
|
|
2
|
+
import { IconButton } from '@mui/material';
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { NsAutocomplete } from '..';
|
|
6
|
+
import { NsIcon } from '../../icon';
|
|
7
|
+
import { OptionUiComponent } from '../mock/MockUI';
|
|
8
|
+
|
|
9
|
+
const PAGE_SIZE = 6;
|
|
10
|
+
|
|
11
|
+
export const RenderAutoComplete = (args: NsAutocompleteProps) => {
|
|
12
|
+
const [selectedOption, setSelectedOption] = useState<string | OptionsProps | null>(null);
|
|
13
|
+
const [searchTerms, setSearchTerms] = useState<string>('');
|
|
14
|
+
const [pageNo, setPageNo] = useState(PAGE_SIZE);
|
|
15
|
+
|
|
16
|
+
const onSeeMoreHandler = () => {
|
|
17
|
+
setPageNo(PAGE_SIZE + PAGE_SIZE);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const AutoCompleteProps = {
|
|
21
|
+
...args,
|
|
22
|
+
seeMore: {
|
|
23
|
+
...args.seeMore,
|
|
24
|
+
onSeeMore: onSeeMoreHandler,
|
|
25
|
+
},
|
|
26
|
+
} as NsAutocompleteProps;
|
|
27
|
+
|
|
28
|
+
const onSelect = (value: string | OptionsProps | null) => {
|
|
29
|
+
setSelectedOption(value);
|
|
30
|
+
setSearchTerms((value = ''));
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleInputChange = (newValue: string) => {
|
|
34
|
+
setSearchTerms(newValue);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const onInputClear = () => {
|
|
38
|
+
setSelectedOption(null);
|
|
39
|
+
setSearchTerms('');
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div>
|
|
44
|
+
<NsAutocomplete
|
|
45
|
+
{...AutoCompleteProps}
|
|
46
|
+
options={args.options.slice(0, pageNo)}
|
|
47
|
+
onChange={handleInputChange}
|
|
48
|
+
onSelect={onSelect}
|
|
49
|
+
OptionsUI={OptionUiComponent}
|
|
50
|
+
iconEnd={
|
|
51
|
+
<IconButton onClick={onInputClear}>
|
|
52
|
+
<NsIcon name="roundedClose" size="xLarge" colorOverride="#F5F5F5" />
|
|
53
|
+
</IconButton>
|
|
54
|
+
}
|
|
55
|
+
value={selectedOption}
|
|
56
|
+
inputValue={searchTerms}
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ShoppingCart } from '@mui/icons-material';
|
|
2
|
+
import Box from '@mui/material/Box';
|
|
3
|
+
import { styled } from '@nuskin/foundation-theme';
|
|
4
|
+
import { NsTypography } from '../';
|
|
5
|
+
import NsBrandWrapper from './NsBrandWrapper';
|
|
6
|
+
import NsBrandLogo from './NsBrandLogo';
|
|
7
|
+
import NsBgCard from './NsBgCard';
|
|
8
|
+
import NsButton from '../button/NsButton';
|
|
9
|
+
import cardSizes from './cardSizes';
|
|
10
|
+
|
|
11
|
+
type NsBrandCardProps = {
|
|
12
|
+
title?: string;
|
|
13
|
+
brandImageUrl: string;
|
|
14
|
+
backgroundImageUrl: string;
|
|
15
|
+
size?: 'small' | 'large';
|
|
16
|
+
button?: boolean;
|
|
17
|
+
onClick?: () => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const CardContainer = styled((props: any) => <Box {...props} />)(({ size }) => {
|
|
21
|
+
const { small, large } = cardSizes;
|
|
22
|
+
let width, height;
|
|
23
|
+
|
|
24
|
+
if (size === 'small') {
|
|
25
|
+
width = small.width;
|
|
26
|
+
height = small.height;
|
|
27
|
+
} else {
|
|
28
|
+
width = large.width;
|
|
29
|
+
height = large.height;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
width,
|
|
34
|
+
display: 'flex',
|
|
35
|
+
alignItems: 'start',
|
|
36
|
+
justifyContent: 'center',
|
|
37
|
+
position: 'relative',
|
|
38
|
+
flexDirection: 'column',
|
|
39
|
+
'.brand-title': {
|
|
40
|
+
marginTop: '15px',
|
|
41
|
+
fontSize: '20px',
|
|
42
|
+
display: 'inline-block',
|
|
43
|
+
wordBreak: 'break-word',
|
|
44
|
+
},
|
|
45
|
+
':hover': {
|
|
46
|
+
'.brand-title': {
|
|
47
|
+
opacity: '0.7',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
'.cta': {
|
|
51
|
+
marginTop: '15px',
|
|
52
|
+
},
|
|
53
|
+
'.image': {
|
|
54
|
+
width,
|
|
55
|
+
height,
|
|
56
|
+
backgroundSize: 'cover',
|
|
57
|
+
backgroundPosition: 'center',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
export default function NsBrandCard({
|
|
62
|
+
title,
|
|
63
|
+
brandImageUrl,
|
|
64
|
+
backgroundImageUrl,
|
|
65
|
+
button,
|
|
66
|
+
size,
|
|
67
|
+
onClick,
|
|
68
|
+
}: NsBrandCardProps) {
|
|
69
|
+
return (
|
|
70
|
+
<CardContainer role="brand-card" component="div" data-testid="brand-card" size={size} onClick={onClick}>
|
|
71
|
+
<NsBgCard backgroundImageUrl={backgroundImageUrl} className="image">
|
|
72
|
+
<NsBrandWrapper>
|
|
73
|
+
<NsBrandLogo component="img" image={brandImageUrl} />
|
|
74
|
+
</NsBrandWrapper>
|
|
75
|
+
</NsBgCard>
|
|
76
|
+
{title ? (
|
|
77
|
+
<NsTypography variant="h3" className="brand-title">
|
|
78
|
+
{title}
|
|
79
|
+
</NsTypography>
|
|
80
|
+
) : null}
|
|
81
|
+
{button ? (
|
|
82
|
+
<NsButton
|
|
83
|
+
className="cta"
|
|
84
|
+
darkModeEnabled={false}
|
|
85
|
+
variant="outlined"
|
|
86
|
+
fullWidth={true}
|
|
87
|
+
endIcon={<ShoppingCart />}
|
|
88
|
+
>
|
|
89
|
+
Add to Cart
|
|
90
|
+
</NsButton>
|
|
91
|
+
) : null}
|
|
92
|
+
</CardContainer>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import Box from '@mui/material/Box';
|
|
2
|
+
import { styled } from '@nuskin/foundation-theme';
|
|
3
|
+
import { NsIcon, NsIconNameType, NsLink } from '../';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
|
|
6
|
+
type NsCheckoutCardProps = {
|
|
7
|
+
icon: NsIconNameType;
|
|
8
|
+
title: string;
|
|
9
|
+
address: string;
|
|
10
|
+
onClick: () => void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const CardContainer = styled(Box)(() => {
|
|
14
|
+
return {
|
|
15
|
+
backgroundColor: '#fff',
|
|
16
|
+
display: 'flex',
|
|
17
|
+
width: '345px',
|
|
18
|
+
height: 'auto',
|
|
19
|
+
border: '2px solid #B2B2B2',
|
|
20
|
+
borderRadius: '2px',
|
|
21
|
+
'.left-bar': {
|
|
22
|
+
width: '4px',
|
|
23
|
+
height: 'auto',
|
|
24
|
+
backgroundColor: '#22D55C',
|
|
25
|
+
},
|
|
26
|
+
'.content': {
|
|
27
|
+
display: 'flex',
|
|
28
|
+
flexDirection: 'row',
|
|
29
|
+
gap: '10px',
|
|
30
|
+
margin: '10px',
|
|
31
|
+
width: '100%',
|
|
32
|
+
},
|
|
33
|
+
'.text-content': {
|
|
34
|
+
width: '100%',
|
|
35
|
+
},
|
|
36
|
+
'.title': {
|
|
37
|
+
fontSize: '14px',
|
|
38
|
+
fontWeight: '700',
|
|
39
|
+
},
|
|
40
|
+
'.address': {
|
|
41
|
+
fontSize: '13px',
|
|
42
|
+
fontWeight: '400',
|
|
43
|
+
},
|
|
44
|
+
'.action-link': {
|
|
45
|
+
color: '#7123D9',
|
|
46
|
+
fontSize: '14px',
|
|
47
|
+
fontWeight: '700',
|
|
48
|
+
letterSpacing: '0.7',
|
|
49
|
+
},
|
|
50
|
+
'.icon-right': {
|
|
51
|
+
marginLeft: 'auto',
|
|
52
|
+
},
|
|
53
|
+
'.icon': {
|
|
54
|
+
':hover': {
|
|
55
|
+
cursor: 'pointer',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export default function NsCheckoutCard({ title, address, icon, onClick }: NsCheckoutCardProps) {
|
|
62
|
+
const [dropdownIcon, setDropdownIcon] = useState(false);
|
|
63
|
+
const handleClick = () => {
|
|
64
|
+
setDropdownIcon(!dropdownIcon);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<CardContainer role="checkout-card" component="div" data-testid="checkout-card" onClick={onClick}>
|
|
69
|
+
<div className="left-bar"></div>
|
|
70
|
+
<div className="content">
|
|
71
|
+
<span>
|
|
72
|
+
<NsIcon name={icon} />
|
|
73
|
+
</span>
|
|
74
|
+
<div className="text-content">
|
|
75
|
+
<article className="title">{title}</article>
|
|
76
|
+
<article className="address">{address}</article>
|
|
77
|
+
<NsLink className="action-link" href={''}>
|
|
78
|
+
CHANGE
|
|
79
|
+
</NsLink>
|
|
80
|
+
</div>
|
|
81
|
+
<span className="icon-right">
|
|
82
|
+
<NsIcon
|
|
83
|
+
name={dropdownIcon ? 'arrowUp' : 'arrowDown'}
|
|
84
|
+
className="icon"
|
|
85
|
+
onClick={handleClick}
|
|
86
|
+
testId="dropdown-icon"
|
|
87
|
+
/>
|
|
88
|
+
</span>
|
|
89
|
+
</div>
|
|
90
|
+
</CardContainer>
|
|
91
|
+
);
|
|
92
|
+
}
|