@nuskin/foundation-ui-components 1.2.0 → 1.4.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 +5 -5
- package/src/common/auth-dropdown/NsAuthDropdown.tsx +13 -9
- package/src/common/button/NsButton.stories.tsx +3 -2
- package/src/common/button/NsButton.tsx +27 -2
- package/src/common/button/types.d.ts +21 -17
- package/src/common/cart-icon/NsCartIcon.spec.tsx +3 -2
- package/src/common/cart-icon/NsCartIcon.stories.tsx +1 -1
- package/src/common/cart-icon/NsCartIcon.styled.tsx +14 -0
- package/src/common/cart-icon/NsCartIcon.tsx +1 -1
- package/src/common/cart-icon/types.d.ts +2 -2
- package/src/common/dropdown/NsDropdown.tsx +15 -11
- package/src/common/email-simple/NsEmailSimple.tsx +1 -1
- package/src/common/flag/NsFlag.spec.tsx +14 -2
- package/src/common/flag/NsFlag.styled.tsx +2 -5
- package/src/common/hooks/useOnClickOutside.tsx +14 -14
- package/src/common/icon/NsIcon.spec.tsx +21 -6
- package/src/common/icon/NsIcon.stories.tsx +2 -1
- package/src/common/icon/NsIcon.styled.tsx +13 -10
- package/src/common/icon/NsIcon.tsx +9 -6
- package/src/common/icon/NsIconVariants.ts +2 -0
- package/src/common/icon/icons/AlertErrorIcon.tsx +2 -1
- package/src/common/icon/icons/ArrowLeft.tsx +2 -2
- package/src/common/icon/icons/ArrowRight.tsx +2 -2
- package/src/common/icon/icons/CartBadge.tsx +2 -2
- package/src/common/icon/icons/Check.tsx +25 -0
- package/src/common/icon/icons/CloseIcon.tsx +1 -1
- package/src/common/icon/icons/ErrorIcon.tsx +3 -2
- package/src/common/icon/icons/SearchIcon.tsx +3 -2
- package/src/common/icon/icons/SuccessIcon.tsx +7 -1
- package/src/common/icon/icons/TrashIcon.tsx +2 -1
- package/src/common/icon/icons/UserIcon.tsx +8 -1
- package/src/common/icon/icons/UserOutline.tsx +4 -1
- package/src/common/icon/icons/WarningIcon.tsx +3 -2
- package/src/common/icon/types.d.ts +3 -2
- package/src/common/index.ts +8 -4
- package/src/common/progress/NsProgress.spec.tsx +2 -2
- package/src/common/progress/NsProgress.stories.tsx +10 -5
- package/src/common/progress/NsProgress.styled.tsx +30 -12
- package/src/common/progress/NsProgress.tsx +18 -16
- package/src/common/progress/types.d.ts +2 -2
- package/src/common/radiogroup/NsRadioGroup.spec.tsx +2 -2
- package/src/common/radiogroup/NsRadioGroup.stories.tsx +2 -2
- package/src/common/radiogroup/NsRadioGroup.tsx +5 -3
- package/src/common/radiogroup/types.d.ts +11 -12
- package/src/common/select/NsSelect.styled.tsx +38 -71
- package/src/common/select/NsSelect.tsx +86 -91
- package/src/common/select/NsSelectOption.tsx +10 -14
- package/src/common/select/specs/NsSelect.spec.tsx +183 -0
- package/src/common/select/specs/NsSelectOption.spec.tsx +32 -0
- package/src/common/select/stories/NsSelect.stories.tsx +25 -80
- package/src/common/select/stories/countries.ts +53 -0
- package/src/common/select/types.d.ts +21 -8
- package/src/common/table/NsTable.stories.tsx +1 -0
- package/src/common/cart-icon/NsCartIcon.styled.ts +0 -14
- package/src/common/select/NsSelect.spec.tsx +0 -274
package/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuskin/foundation-ui-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "It is a foundation library written in react, that provides common components to all UI pages",
|
|
5
|
-
"private": false,
|
|
6
5
|
"main": "src/common/index.ts",
|
|
7
6
|
"repository": {
|
|
8
7
|
"type": "git",
|
|
@@ -37,7 +36,7 @@
|
|
|
37
36
|
"test:pattern": "jest --coverage --testNamePattern accordion/*",
|
|
38
37
|
"test:watch": "jest --watch",
|
|
39
38
|
"test:failed": "npx jest --onlyFailures",
|
|
40
|
-
"test:coverage": "
|
|
39
|
+
"test:coverage": "jest --coverage",
|
|
41
40
|
"types:check": "tsc --pretty --noEmit"
|
|
42
41
|
},
|
|
43
42
|
"dependencies": {
|
|
@@ -47,7 +46,7 @@
|
|
|
47
46
|
"@mui/material": "5.4.2",
|
|
48
47
|
"@mui/utils": "^5.14.1",
|
|
49
48
|
"@next/font": "13.2.4",
|
|
50
|
-
"@nuskin/foundation-theme": "1.0.0
|
|
49
|
+
"@nuskin/foundation-theme": "^1.0.0",
|
|
51
50
|
"framer-motion": "^10.8.5",
|
|
52
51
|
"next": "13.2.4",
|
|
53
52
|
"react": "18.2.0",
|
|
@@ -103,5 +102,6 @@
|
|
|
103
102
|
"files": [
|
|
104
103
|
"src/common"
|
|
105
104
|
],
|
|
106
|
-
"readme": "./README.md"
|
|
105
|
+
"readme": "./README.md",
|
|
106
|
+
"packageManager": "yarn@3.6.3"
|
|
107
107
|
}
|
|
@@ -25,27 +25,31 @@ const NsAuthDropdown = ({
|
|
|
25
25
|
darkModeEnabled = true,
|
|
26
26
|
}: NsAuthDropdownProps) => {
|
|
27
27
|
const [isOpen, setIsOpen] = useState(false);
|
|
28
|
-
const
|
|
28
|
+
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
|
29
29
|
|
|
30
|
-
const handleToggle = () => {
|
|
30
|
+
const handleToggle = useCallback(() => {
|
|
31
31
|
setIsOpen(!isOpen);
|
|
32
|
-
};
|
|
32
|
+
}, [isOpen]);
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
const handleClose = useCallback(() => {
|
|
35
|
+
setIsOpen(false);
|
|
36
|
+
},[]);
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
useOnClickOutside(dropdownRef?.current, handleClose);
|
|
39
|
+
|
|
40
|
+
const handleOnMouseLeave = useCallback(() => {
|
|
37
41
|
if (closeOnMouseLeave) {
|
|
38
|
-
|
|
42
|
+
handleClose();
|
|
39
43
|
}
|
|
40
|
-
}, [closeOnMouseLeave]);
|
|
44
|
+
}, [closeOnMouseLeave, handleClose]);
|
|
41
45
|
|
|
42
46
|
return (
|
|
43
|
-
<AuthDropdownContainer ref={
|
|
47
|
+
<AuthDropdownContainer ref={dropdownRef}>
|
|
44
48
|
<AuthHead data-testid="auth-head" onClick={handleToggle} darkModeEnabled={darkModeEnabled}>
|
|
45
49
|
<Button>{titleText}</Button>
|
|
46
50
|
<NsIcon testId="chevron-icon" name={isOpen ? 'arrowUp' : 'arrowDown'} color="primary" />
|
|
47
51
|
{isOpen ? (
|
|
48
|
-
<div onMouseLeave={
|
|
52
|
+
<div onMouseLeave={handleOnMouseLeave} className="auth-wrapper">
|
|
49
53
|
<Triangle />
|
|
50
54
|
<AuthDropdown>
|
|
51
55
|
<SignInButton href={signInRedirectLink} data-testid="sign-in-btn">
|
|
@@ -6,7 +6,6 @@ import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
|
|
|
6
6
|
import { within } from '@storybook/testing-library';
|
|
7
7
|
import { expect } from '@storybook/jest';
|
|
8
8
|
import { nuskinTheme } from '@nuskin/foundation-theme';
|
|
9
|
-
|
|
10
9
|
const { palette } = nuskinTheme;
|
|
11
10
|
|
|
12
11
|
type ComponentType = typeof NsButton;
|
|
@@ -40,7 +39,9 @@ export default {
|
|
|
40
39
|
},
|
|
41
40
|
} as MetaStory;
|
|
42
41
|
|
|
43
|
-
export const button: Story = {
|
|
42
|
+
export const button: Story = {
|
|
43
|
+
args: {},
|
|
44
|
+
};
|
|
44
45
|
|
|
45
46
|
export const IconOnlyButton: Story = {
|
|
46
47
|
render: (args: any) => (
|
|
@@ -4,13 +4,38 @@ import { useTheme } from '@mui/material/styles';
|
|
|
4
4
|
import StyledButton from './NsButton.styled';
|
|
5
5
|
|
|
6
6
|
import { NsButtonProps } from './types.d';
|
|
7
|
+
import { NsIcon } from '../icon';
|
|
7
8
|
|
|
8
9
|
const NsButton = ({ children, darkModeEnabled = true, ...props }: PropsWithChildren<NsButtonProps>) => {
|
|
9
|
-
const { color } = props;
|
|
10
|
+
const { color, startIconName, endIconName, variant } = props;
|
|
10
11
|
const { palette } = useTheme();
|
|
11
12
|
const btnColor = color && palette?.[color] ? color : 'primary';
|
|
13
|
+
let iconColor = btnColor;
|
|
14
|
+
if (variant === 'contained') {
|
|
15
|
+
if (btnColor === 'secondary') {
|
|
16
|
+
iconColor = 'primary';
|
|
17
|
+
} else {
|
|
18
|
+
iconColor = 'secondary';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
let startIcon = props.startIcon;
|
|
22
|
+
if (!startIcon && startIconName) {
|
|
23
|
+
startIcon = <NsIcon name={startIconName} color={iconColor} size={props.size} />;
|
|
24
|
+
}
|
|
25
|
+
let endIcon = props.endIcon;
|
|
26
|
+
if (!endIcon && endIconName) {
|
|
27
|
+
endIcon = <NsIcon name={endIconName} color={iconColor} size={props.size} />;
|
|
28
|
+
}
|
|
29
|
+
|
|
12
30
|
return (
|
|
13
|
-
<StyledButton
|
|
31
|
+
<StyledButton
|
|
32
|
+
{...props}
|
|
33
|
+
startIcon={startIcon}
|
|
34
|
+
endIcon={endIcon}
|
|
35
|
+
color={btnColor}
|
|
36
|
+
disableRipple
|
|
37
|
+
darkModeEnabled={darkModeEnabled}
|
|
38
|
+
>
|
|
14
39
|
{children}
|
|
15
40
|
</StyledButton>
|
|
16
41
|
);
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import { ButtonProps as MUIButtonProps } from '@mui/material';
|
|
2
|
+
import { NsIconProps } from '../icon';
|
|
2
3
|
|
|
3
4
|
export type NsButtonProps = Pick<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
5
|
+
MUIButtonProps,
|
|
6
|
+
| 'aria-label'
|
|
7
|
+
| 'className'
|
|
8
|
+
| 'color'
|
|
9
|
+
| 'disabled'
|
|
10
|
+
| 'endIcon'
|
|
11
|
+
| 'fullWidth'
|
|
12
|
+
| 'href'
|
|
13
|
+
| 'onBlur'
|
|
14
|
+
| 'onClick'
|
|
15
|
+
| 'onChange'
|
|
16
|
+
| 'onFocus'
|
|
17
|
+
| 'size'
|
|
18
|
+
| 'startIcon'
|
|
19
|
+
| 'type'
|
|
20
|
+
| 'variant'
|
|
19
21
|
> & {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
iconOnly?: boolean;
|
|
23
|
+
darkModeEnabled?: boolean;
|
|
24
|
+
startIconName?: NsIconProps['name'];
|
|
25
|
+
endIconName?: NsIconProps['name'];
|
|
22
26
|
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { NsCartIcon } from './';
|
|
2
1
|
import { render, screen } from '@/test-utils/index';
|
|
3
2
|
|
|
3
|
+
import { NsCartIcon } from './';
|
|
4
|
+
|
|
4
5
|
const { getByTestId } = screen;
|
|
5
6
|
describe('NsCartIcon', () => {
|
|
6
7
|
it('1. renders Cart icon when item count is 0', () => {
|
|
7
|
-
render(<NsCartIcon
|
|
8
|
+
render(<NsCartIcon />);
|
|
8
9
|
const cartIcon = getByTestId('cart-badge-icon');
|
|
9
10
|
expect(cartIcon).toBeInTheDocument();
|
|
10
11
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Badge, Theme } from '@mui/material';
|
|
2
|
+
import { ColorUtils, styled, FoundationTheme } from '@nuskin/foundation-theme';
|
|
3
|
+
|
|
4
|
+
type ItemCountParams = {
|
|
5
|
+
theme?: Theme;
|
|
6
|
+
bgcolor: FoundationTheme.PaletteColors;
|
|
7
|
+
};
|
|
8
|
+
const { paletteColor } = ColorUtils;
|
|
9
|
+
export const StyledBadge = styled(Badge)<ItemCountParams>`
|
|
10
|
+
& .MuiBadge-badge {
|
|
11
|
+
background-color: ${({ bgcolor, theme }) => paletteColor(bgcolor)({ theme })};
|
|
12
|
+
color: ${({ bgcolor, theme }) => paletteColor(bgcolor, 'contrastText')({ theme })};
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
@@ -4,7 +4,7 @@ import { NsCartIconProps } from './types.d';
|
|
|
4
4
|
|
|
5
5
|
const NsCartIcon = ({ itemCount = 0, size = 'large', color = 'primary', maxItemCount = 999 }: NsCartIconProps) => {
|
|
6
6
|
return (
|
|
7
|
-
<StyledBadge badgeContent={itemCount} max={maxItemCount} bgcolor={color}>
|
|
7
|
+
<StyledBadge badgeContent={itemCount} max={maxItemCount} bgcolor={color} showZero>
|
|
8
8
|
<NsIcon testId="cart-badge-icon" name="cart" size={size} color={color} />
|
|
9
9
|
</StyledBadge>
|
|
10
10
|
);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { FoundationTheme } from '@nuskin/foundation-theme';
|
|
2
2
|
|
|
3
3
|
export type NsCartIconProps = {
|
|
4
|
-
itemCount
|
|
4
|
+
itemCount?: number;
|
|
5
5
|
size?: 'small' | 'medium' | 'large' | 'xLarge';
|
|
6
6
|
color?: FoundationTheme.PaletteColors;
|
|
7
|
-
maxItemCount?:number;
|
|
7
|
+
maxItemCount?: number;
|
|
8
8
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useRef, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { DropdownContainer, DropdownButton, DropdownList, DropdownHead } from './NsDropdown.styled';
|
|
4
4
|
import { NsIcon } from '../icon';
|
|
@@ -19,20 +19,24 @@ const NsDropdown: React.FC<DropdownProps> = ({
|
|
|
19
19
|
}) => {
|
|
20
20
|
const [isOpen, setIsOpen] = useState(false);
|
|
21
21
|
const [value, setValue] = useState(selectedValue || '');
|
|
22
|
-
const
|
|
22
|
+
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
|
23
23
|
|
|
24
|
-
const handleToggle = () => {
|
|
24
|
+
const handleToggle = useCallback(() => {
|
|
25
25
|
if (disabled) return;
|
|
26
|
-
setIsOpen(!
|
|
27
|
-
};
|
|
26
|
+
setIsOpen((x) => !x);
|
|
27
|
+
}, [disabled]);
|
|
28
|
+
|
|
29
|
+
const handleClose = useCallback(() => {
|
|
30
|
+
setIsOpen(false);
|
|
31
|
+
}, []);
|
|
28
32
|
|
|
29
|
-
useOnClickOutside(
|
|
33
|
+
useOnClickOutside(dropdownRef?.current, handleClose);
|
|
30
34
|
|
|
31
|
-
const
|
|
35
|
+
const handleOnMouseLeave = useCallback(() => {
|
|
32
36
|
if (closeOnMouseLeave) {
|
|
33
|
-
|
|
37
|
+
handleClose();
|
|
34
38
|
}
|
|
35
|
-
};
|
|
39
|
+
}, [closeOnMouseLeave, handleClose]);
|
|
36
40
|
|
|
37
41
|
const handleSelect = (input: string) => {
|
|
38
42
|
onSelect(input);
|
|
@@ -41,7 +45,7 @@ const NsDropdown: React.FC<DropdownProps> = ({
|
|
|
41
45
|
};
|
|
42
46
|
|
|
43
47
|
return (
|
|
44
|
-
<DropdownContainer disabled={disabled} ref={
|
|
48
|
+
<DropdownContainer disabled={disabled} ref={dropdownRef}>
|
|
45
49
|
<DropdownHead data-testid="dropdown-head" disabled={disabled} type={type} onClick={handleToggle}>
|
|
46
50
|
<DropdownButton>{selectedLabel || placeholder}</DropdownButton>
|
|
47
51
|
<NsIcon testId="chevron-icon" name={isOpen ? 'arrowUp' : 'arrowDown'} color="primary" />
|
|
@@ -49,7 +53,7 @@ const NsDropdown: React.FC<DropdownProps> = ({
|
|
|
49
53
|
{isOpen ? (
|
|
50
54
|
<DropdownList
|
|
51
55
|
data-testid="dropdown-list"
|
|
52
|
-
onMouseLeave={
|
|
56
|
+
onMouseLeave={handleOnMouseLeave}
|
|
53
57
|
borderColor={dropdownBorderColor}
|
|
54
58
|
>
|
|
55
59
|
{React.Children.map(children, (child) => {
|
|
@@ -43,7 +43,7 @@ const NsEmailSimple = (props: NsEmailSimpleProps) => {
|
|
|
43
43
|
errorMessageValue = '';
|
|
44
44
|
}
|
|
45
45
|
} catch (error) {
|
|
46
|
-
console.
|
|
46
|
+
console.error('API error', error);
|
|
47
47
|
}
|
|
48
48
|
} else {
|
|
49
49
|
//TODO What happens when user does not pass valid checkEmail function
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { render } from '@/test-utils/index';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
|
|
3
|
-
import { render } from '@/test-utils/index';
|
|
4
4
|
import { NsFlag } from '../';
|
|
5
5
|
|
|
6
6
|
describe('FlagComponent', () => {
|
|
7
|
-
it('renders a div element with background color and text', () => {
|
|
7
|
+
it('1. renders a div element with background color and text', () => {
|
|
8
8
|
const text = 'NU SKIN';
|
|
9
9
|
const { getByText } = render(<NsFlag message="NU SKIN" color="accentColorA" />);
|
|
10
10
|
const divElement = getByText(text);
|
|
@@ -12,4 +12,16 @@ describe('FlagComponent', () => {
|
|
|
12
12
|
expect(divElement).toBeInTheDocument();
|
|
13
13
|
expect(divElement).toHaveTextContent(text);
|
|
14
14
|
});
|
|
15
|
+
it('2. should render a flag with default size, color, and testId when only message prop is provided', () => {
|
|
16
|
+
const props = {
|
|
17
|
+
message: 'Test Message',
|
|
18
|
+
};
|
|
19
|
+
const { getByTestId } = render(<NsFlag {...props} />);
|
|
20
|
+
const flag = getByTestId('ns-flag-component');
|
|
21
|
+
expect(flag).toBeInTheDocument();
|
|
22
|
+
expect(flag).toHaveStyle('height: 20px');
|
|
23
|
+
expect(flag).toHaveStyle('color: #ffffff');
|
|
24
|
+
expect(flag).toHaveStyle('background: #f56767');
|
|
25
|
+
expect(flag).toHaveTextContent(props.message);
|
|
26
|
+
});
|
|
15
27
|
});
|
|
@@ -27,7 +27,7 @@ const FlagHeightMap = {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const getFlagHeight = ({ $size }: { $size: NsFlagSize }) => {
|
|
30
|
-
return FlagHeightMap[$size]
|
|
30
|
+
return FlagHeightMap[$size];
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
const makeFlagSize = ({ $size }: { $size: NsFlagSize }) => {
|
|
@@ -36,10 +36,7 @@ const makeFlagSize = ({ $size }: { $size: NsFlagSize }) => {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
const makeFlagBorder = ({ $size, $bgColor = 'accentColorA', theme }: NsFlagThemeParams) => {
|
|
39
|
-
|
|
40
|
-
return `${makeFlagSize({ $size })}px solid ${paletteColor($bgColor)({ theme })}`;
|
|
41
|
-
}
|
|
42
|
-
return '';
|
|
39
|
+
return `${makeFlagSize({ $size })}px solid ${paletteColor($bgColor)({ theme })}`;
|
|
43
40
|
};
|
|
44
41
|
|
|
45
42
|
export const StyledFlag = styled('span')<NsFlagThemeParams>`
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
export type Handler = () => void;
|
|
4
4
|
|
|
5
|
-
export function useOnClickOutside
|
|
6
|
-
|
|
7
|
-
handler: Handler,
|
|
5
|
+
export function useOnClickOutside(
|
|
6
|
+
element: HTMLDivElement | null,
|
|
7
|
+
handler: Handler = () => {},
|
|
8
8
|
mouseEvent: 'click' | 'mousedown' | 'mouseup' = 'click'
|
|
9
9
|
): void {
|
|
10
|
-
const handleEventListener = (event: MouseEvent) => {
|
|
11
|
-
const el = ref?.current;
|
|
12
|
-
if (!el || el.contains(event.target as Node)) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
handler();
|
|
16
|
-
};
|
|
17
10
|
useEffect(() => {
|
|
11
|
+
const eventListener = (event: MouseEvent) => {
|
|
12
|
+
// Do nothing if clicking ref's element or descendent elements
|
|
13
|
+
if (!element || element.contains(event.target as Node)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
handler?.();
|
|
17
|
+
};
|
|
18
18
|
if (document) {
|
|
19
|
-
document.addEventListener(mouseEvent,
|
|
19
|
+
document.addEventListener(mouseEvent, eventListener);
|
|
20
20
|
return () => {
|
|
21
|
-
document.removeEventListener(mouseEvent,
|
|
21
|
+
document.removeEventListener(mouseEvent, eventListener);
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
-
}, []);
|
|
24
|
+
}, [mouseEvent, handler, element]);
|
|
25
25
|
}
|
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { render, screen } from '@/test-utils/index';
|
|
2
|
+
import { fireEvent } from '@storybook/testing-library';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
3
5
|
import { NsIcon, NsIconNameType, NsIconVariants } from './';
|
|
4
6
|
|
|
5
7
|
describe('NsIcon Component', () => {
|
|
6
|
-
it('renders cartBadge icon with custom size and color', () => {
|
|
7
|
-
const { getByTestId } = render(<NsIcon name="cart" size=
|
|
8
|
+
it('1. renders cartBadge icon with custom size and color', () => {
|
|
9
|
+
const { getByTestId } = render(<NsIcon name="cart" size={'medium'} color="secondary" />);
|
|
8
10
|
const icon = getByTestId('icon');
|
|
9
11
|
const svgIcon = icon.querySelector('svg');
|
|
10
12
|
// expect(svgIcon).toHaveClass('MuiSvgIcon-colorSecondary');
|
|
11
13
|
expect(svgIcon).toHaveClass('MuiSvgIcon-fontSizeMedium');
|
|
12
14
|
});
|
|
13
15
|
|
|
14
|
-
it('renders with testId', () => {
|
|
16
|
+
it('2. renders with testId', () => {
|
|
15
17
|
Object.keys(NsIconVariants).forEach((iconName) => {
|
|
16
18
|
render(<NsIcon name={iconName as NsIconNameType} />);
|
|
17
19
|
const icon = screen.getByTestId(iconName);
|
|
18
20
|
expect(icon).toBeInTheDocument();
|
|
19
21
|
});
|
|
20
22
|
});
|
|
21
|
-
it('renders with custom testId', () => {
|
|
23
|
+
it('3. renders with custom testId', () => {
|
|
22
24
|
const { getByTestId } = render(<NsIcon name="cart" testId="custom-icon" />);
|
|
23
25
|
const icon = getByTestId('custom-icon');
|
|
24
26
|
expect(icon).toBeInTheDocument();
|
|
25
27
|
expect(icon.innerHTML).toContain('svg');
|
|
26
28
|
});
|
|
27
29
|
|
|
28
|
-
it('throws an error if invalid icon name is provided', () => {
|
|
30
|
+
it('4. throws an error if invalid icon name is provided', () => {
|
|
29
31
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
30
32
|
// @ts-ignore
|
|
31
33
|
const { queryByTestId } = render(<NsIcon name="invalidIcon" />);
|
|
@@ -33,4 +35,17 @@ describe('NsIcon Component', () => {
|
|
|
33
35
|
expect(consoleErrorSpy).not.toHaveBeenCalled();
|
|
34
36
|
consoleErrorSpy.mockRestore();
|
|
35
37
|
});
|
|
38
|
+
it('5. renders default color of alertErrorIcon', () => {
|
|
39
|
+
const { getByTestId } = render(<NsIcon name="alertError" size={'medium'} />);
|
|
40
|
+
const icon = getByTestId('alertError');
|
|
41
|
+
expect(icon).toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
it('6. calls onClick event handler', () => {
|
|
44
|
+
const onClick = jest.fn();
|
|
45
|
+
const { getByTestId } = render(<NsIcon name="alertError" size="medium" onClick={onClick} />);
|
|
46
|
+
const icon = getByTestId('alertError');
|
|
47
|
+
expect(icon).toBeInTheDocument();
|
|
48
|
+
fireEvent.click(icon);
|
|
49
|
+
expect(onClick).toHaveBeenCalled();
|
|
50
|
+
});
|
|
36
51
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PaletteColors } from '@nuskin/foundation-theme';
|
|
1
2
|
import type { StoryObj, Meta } from '@storybook/react';
|
|
2
3
|
import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
|
|
3
4
|
import { NsIcon, NsIconVariants } from './';
|
|
@@ -18,7 +19,7 @@ export default {
|
|
|
18
19
|
},
|
|
19
20
|
color: {
|
|
20
21
|
control: 'select',
|
|
21
|
-
options:
|
|
22
|
+
options: PaletteColors,
|
|
22
23
|
},
|
|
23
24
|
colorOverride: {
|
|
24
25
|
control: 'color',
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { styled } from '@nuskin/foundation-theme';
|
|
2
2
|
import { NsIconSizeType } from './types.d';
|
|
3
3
|
|
|
4
|
-
const iconSize = {
|
|
4
|
+
export const iconSize: Record<NsIconSizeType, string> = {
|
|
5
5
|
small: '16px',
|
|
6
6
|
medium: '20px',
|
|
7
7
|
large: '24px',
|
|
8
8
|
xLarge: '30px',
|
|
9
9
|
};
|
|
10
|
+
type IconWrapperType = { size: NsIconSizeType };
|
|
10
11
|
|
|
11
|
-
export const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
export const IconWrapper = styled('div')<IconWrapperType>(({ size }) => {
|
|
13
|
+
return {
|
|
14
|
+
display: 'flex',
|
|
15
|
+
placeContent: 'center',
|
|
16
|
+
placeItems: 'center',
|
|
17
|
+
svg: {
|
|
18
|
+
width: iconSize[size],
|
|
19
|
+
height: iconSize[size],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
});
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { useTheme } from '@mui/material';
|
|
2
2
|
import { ColorUtils } from '@nuskin/foundation-theme';
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import { IconWrapper } from './NsIcon.styled';
|
|
4
5
|
import IconVariants from './NsIconVariants';
|
|
5
|
-
import { NsIconProps } from './types.d';
|
|
6
|
+
import { NsIconNameType, NsIconProps } from './types.d';
|
|
6
7
|
|
|
7
8
|
const { paletteColor } = ColorUtils;
|
|
9
|
+
|
|
8
10
|
const NsIcon = ({
|
|
9
11
|
name,
|
|
10
|
-
size
|
|
12
|
+
size,
|
|
11
13
|
color = 'primary',
|
|
12
14
|
testId = 'icon',
|
|
13
15
|
className,
|
|
14
16
|
colorOverride,
|
|
17
|
+
onClick = () => {},
|
|
15
18
|
}: NsIconProps) => {
|
|
16
|
-
const Icon = IconVariants[name];
|
|
19
|
+
const Icon = IconVariants[name as NsIconNameType];
|
|
17
20
|
const theme = useTheme();
|
|
18
21
|
const iconColor = paletteColor(color)({ theme });
|
|
19
22
|
if (!Icon) {
|
|
@@ -21,9 +24,9 @@ const NsIcon = ({
|
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
return (
|
|
24
|
-
<
|
|
27
|
+
<IconWrapper data-testid={testId} size={size || 'large'} className={className} onClick={onClick}>
|
|
25
28
|
<Icon color={colorOverride ? colorOverride : (iconColor as any)} />
|
|
26
|
-
</
|
|
29
|
+
</IconWrapper>
|
|
27
30
|
);
|
|
28
31
|
};
|
|
29
32
|
|
|
@@ -20,6 +20,7 @@ import CloseIcon from './icons/CloseIcon';
|
|
|
20
20
|
import UserIcon from './icons/UserIcon';
|
|
21
21
|
import HamburgerIcon from './icons/HamburgerIcon';
|
|
22
22
|
import UserOutline from './icons/UserOutline';
|
|
23
|
+
import Check from './icons/Check';
|
|
23
24
|
|
|
24
25
|
const IconVariants = {
|
|
25
26
|
arrowDown: ArrowDown,
|
|
@@ -30,6 +31,7 @@ const IconVariants = {
|
|
|
30
31
|
cart: Cart,
|
|
31
32
|
cartBadge: CartBadge,
|
|
32
33
|
close: CloseIcon,
|
|
34
|
+
check: Check,
|
|
33
35
|
checkShield: CheckShield,
|
|
34
36
|
checkVerified: CheckVerified,
|
|
35
37
|
error: ErrorIcon,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
2
|
import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon';
|
|
3
|
+
|
|
3
4
|
/**
|
|
4
5
|
*
|
|
5
6
|
* @param props { fill: color to be filled for icon, color: color of the tick icon}
|
|
@@ -24,7 +25,7 @@ const AlertErrorIcon = (props: SvgIconProps) => {
|
|
|
24
25
|
15.5143276 0.328282319,15.6093523 0.505163987,15.6123832 L17.5120236,15.6123832 C17.6889052,15.6093523 17.8513135,15.5143276 17.940195,15.3618609 C18.0290766,
|
|
25
26
|
15.2093943 18.0314841,15.0216954 17.9465423,14.8670135 Z"
|
|
26
27
|
id="Shape"
|
|
27
|
-
fill={props.color
|
|
28
|
+
fill={props.color}
|
|
28
29
|
fillRule="nonzero"
|
|
29
30
|
></path>
|
|
30
31
|
<path
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SvgIcon, SvgIconProps } from '@mui/material';
|
|
2
2
|
|
|
3
3
|
const ArrowLeft = (props: SvgIconProps) => {
|
|
4
4
|
return (
|
|
@@ -19,7 +19,7 @@ const ArrowLeft = (props: SvgIconProps) => {
|
|
|
19
19
|
strokeLinecap="round"
|
|
20
20
|
strokeLinejoin="round"
|
|
21
21
|
>
|
|
22
|
-
<g transform="translate(-196, -226)" stroke={props.color
|
|
22
|
+
<g transform="translate(-196, -226)" stroke={props.color}>
|
|
23
23
|
<polyline
|
|
24
24
|
// eslint-disable-next-line max-len
|
|
25
25
|
transform="translate(209.000000, 237.500000) scale(-1.2, 1.2) rotate(90.000000) translate(-209.000000, -237.500000)"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SvgIcon, SvgIconProps } from '@mui/material';
|
|
2
2
|
|
|
3
3
|
export const ArrowRight = (props: SvgIconProps) => {
|
|
4
4
|
return (
|
|
@@ -19,7 +19,7 @@ export const ArrowRight = (props: SvgIconProps) => {
|
|
|
19
19
|
strokeLinecap="round"
|
|
20
20
|
strokeLinejoin="round"
|
|
21
21
|
>
|
|
22
|
-
<g transform="translate(-196, -226)" stroke={props.color
|
|
22
|
+
<g transform="translate(-196, -226)" stroke={props.color}>
|
|
23
23
|
<polyline
|
|
24
24
|
// eslint-disable-next-line max-len
|
|
25
25
|
transform="translate(209.000000, 237.500000) scale(-1.2, 1.2) rotate(-90.000000) translate(-209.000000, -237.500000)"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { SvgIcon, SvgIconProps } from '@mui/material';
|
|
4
4
|
|
|
5
5
|
const CartBadge = (props: SvgIconProps) => {
|
|
6
6
|
return (
|
|
@@ -20,7 +20,7 @@ const CartBadge = (props: SvgIconProps) => {
|
|
|
20
20
|
width="30px"
|
|
21
21
|
height="30px"
|
|
22
22
|
>
|
|
23
|
-
<g id="Group" transform="translate(0.000000, 0.500000)" stroke={props.color
|
|
23
|
+
<g id="Group" transform="translate(0.000000, 0.500000)" stroke={props.color}>
|
|
24
24
|
<circle id="Oval" cx="11" cy="21.5" r="1.5"></circle>
|
|
25
25
|
<circle id="Oval" cx="23" cy="21.5" r="1.5"></circle>
|
|
26
26
|
<polyline
|