@nuskin/foundation-ui-components 1.1.0 → 1.2.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.
Files changed (147) hide show
  1. package/package.json +18 -14
  2. package/src/common/accordion/NsAccordion.tsx +32 -40
  3. package/src/common/accordion/NsAccordionHeader.tsx +29 -8
  4. package/src/common/accordion/NsAccordionItem.tsx +24 -4
  5. package/src/common/accordion/NsCollapse.tsx +96 -35
  6. package/src/common/accordion/NsToggleSection.tsx +16 -3
  7. package/src/common/accordion/ToggleSection.styled.tsx +11 -7
  8. package/src/common/accordion/colorMap.ts +117 -0
  9. package/src/common/accordion/enums.ts +18 -0
  10. package/src/common/accordion/index.ts +1 -0
  11. package/src/common/accordion/specs/NsAccordion.spec.tsx +101 -31
  12. package/src/common/accordion/specs/NsAccordionHeader.spec.tsx +77 -10
  13. package/src/common/accordion/specs/NsAccordionItem.spec.tsx +27 -10
  14. package/src/common/accordion/specs/NsCollapse.spec.tsx +109 -17
  15. package/src/common/accordion/specs/NsToggleSection.spec.tsx +50 -14
  16. package/src/common/accordion/stories/NsAccordion.stories.tsx +31 -13
  17. package/src/common/accordion/stories/NsAccordionHeader.stories.tsx +5 -1
  18. package/src/common/accordion/stories/NsAccordionItem.stories.tsx +8 -3
  19. package/src/common/accordion/stories/NsCollapse.stories.tsx +19 -4
  20. package/src/common/accordion/stories/NsToggleSection.stories.tsx +6 -3
  21. package/src/common/accordion/stories/data.tsx +140 -19
  22. package/src/common/accordion/types.d.ts +55 -47
  23. package/src/common/alert/Header.tsx +29 -0
  24. package/src/common/alert/NsAlert.spec.tsx +46 -13
  25. package/src/common/alert/NsAlert.stories.tsx +1 -1
  26. package/src/common/alert/{NsAlert.styled.ts → NsAlert.styled.tsx} +22 -22
  27. package/src/common/alert/NsAlert.tsx +22 -87
  28. package/src/common/alert/types.d.ts +7 -7
  29. package/src/common/auth-dropdown/NsAuthDropdown.spec.tsx +23 -5
  30. package/src/common/auth-dropdown/NsAuthDropdown.styled.ts +9 -2
  31. package/src/common/auth-dropdown/NsAuthDropdown.tsx +11 -6
  32. package/src/common/autocomplete/NsAutocomplete.spec.tsx +37 -14
  33. package/src/common/autocomplete/NsAutocomplete.stories.tsx +18 -2
  34. package/src/common/autocomplete/NsAutocomplete.tsx +63 -24
  35. package/src/common/autocomplete/mock/MockData.ts +51 -7
  36. package/src/common/autocomplete/mock/MockUI.tsx +2 -1
  37. package/src/common/autocomplete/types.d.ts +7 -4
  38. package/src/common/banner/NsBanner.spec.tsx +12 -3
  39. package/src/common/brand-components/NsBrandComponent.tsx +2 -2
  40. package/src/common/breadcrumb/NsBreadcrumb.spec.tsx +8 -3
  41. package/src/common/breadcrumb/data.ts +8 -5
  42. package/src/common/button/NsButton.spec.tsx +65 -49
  43. package/src/common/button/NsButton.stories.tsx +39 -4
  44. package/src/common/button/NsButton.styled.tsx +49 -66
  45. package/src/common/button/NsButton.tsx +1 -2
  46. package/src/common/button/helper.ts +82 -0
  47. package/src/common/card/NsFfgCard.tsx +13 -7
  48. package/src/common/card/specs/NsFfgCard.spec.tsx +18 -8
  49. package/src/common/card/stories/NsFfgCard.stories.tsx +47 -14
  50. package/src/common/card/styled/NsFfgStyled.tsx +9 -7
  51. package/src/common/card/types.d.ts +7 -4
  52. package/src/common/cart-icon/NsCartIcon.spec.tsx +7 -18
  53. package/src/common/cart-icon/NsCartIcon.stories.tsx +1 -1
  54. package/src/common/cart-icon/NsCartIcon.styled.ts +7 -33
  55. package/src/common/cart-icon/NsCartIcon.tsx +5 -14
  56. package/src/common/cart-icon/types.d.ts +1 -0
  57. package/src/common/checkbox/NsCheckbox.styled.tsx +15 -16
  58. package/src/common/checkbox/NsCheckbox.tsx +1 -1
  59. package/src/common/checkbox/types.d.ts +1 -1
  60. package/src/common/column-control/styled/common.ts +1 -1
  61. package/src/common/column-control/utils.ts +4 -4
  62. package/src/common/common.types.d.ts +14 -5
  63. package/src/common/dropdown/NsDropdown.styled.tsx +23 -18
  64. package/src/common/dropdown/NsDropdown.tsx +7 -2
  65. package/src/common/hooks/index.ts +2 -0
  66. package/src/common/hooks/specs/useWindowSize.spec.tsx +19 -0
  67. package/src/common/hooks/useAccordionMap.tsx +39 -0
  68. package/src/common/hooks/useOnClickOutside.tsx +25 -0
  69. package/src/common/hooks/useWindowSize.tsx +30 -0
  70. package/src/common/icon/NsIcon.spec.tsx +1 -1
  71. package/src/common/icon/NsIcon.stories.tsx +3 -0
  72. package/src/common/icon/NsIcon.tsx +6 -2
  73. package/src/common/icon/NsIconVariants.ts +3 -0
  74. package/src/common/icon/icons/AlertErrorIcon.tsx +2 -2
  75. package/src/common/icon/icons/ArrowLeft.tsx +3 -6
  76. package/src/common/icon/icons/ArrowRight.tsx +6 -8
  77. package/src/common/icon/icons/Cart.tsx +7 -32
  78. package/src/common/icon/icons/CartBadge.tsx +2 -2
  79. package/src/common/icon/icons/CloseIcon.tsx +1 -1
  80. package/src/common/icon/icons/ErrorIcon.tsx +2 -2
  81. package/src/common/icon/icons/HamburgerIcon.tsx +5 -2
  82. package/src/common/icon/icons/SearchIcon.tsx +1 -1
  83. package/src/common/icon/icons/SuccessIcon.tsx +2 -7
  84. package/src/common/icon/icons/TrashIcon.tsx +1 -1
  85. package/src/common/icon/icons/UserIcon.tsx +3 -6
  86. package/src/common/icon/icons/UserOutline.tsx +24 -0
  87. package/src/common/icon/icons/WarningIcon.tsx +1 -1
  88. package/src/common/icon/icons/home.tsx +1 -1
  89. package/src/common/image/NsImage.spec.tsx +56 -6
  90. package/src/common/image/NsImage.tsx +23 -11
  91. package/src/common/index.ts +7 -3
  92. package/src/common/input/InputWrapper.tsx +12 -7
  93. package/src/common/input/NsInputField.tsx +1 -1
  94. package/src/common/label/NsLabel.stories.tsx +0 -3
  95. package/src/common/label/NsLabel.tsx +1 -1
  96. package/src/common/modal/NsModal.spec.tsx +46 -0
  97. package/src/common/modal/NsModal.styled.tsx +28 -0
  98. package/src/common/modal/NsModal.tsx +37 -0
  99. package/src/common/modal/helper-component/NsModalContentBox.tsx +32 -0
  100. package/src/common/modal/helper-component/NsModalDefault.tsx +25 -0
  101. package/src/common/modal/helper-component/NsModalStatus.tsx +15 -0
  102. package/src/common/modal/helper-component/NsModalStatusIcon.tsx +14 -0
  103. package/src/common/modal/helper-component/NsModalTitle.tsx +26 -0
  104. package/src/common/modal/index.ts +2 -0
  105. package/src/common/modal/model/NsModalVariantMap.tsx +24 -0
  106. package/src/common/modal/stories/NsModal.stories.tsx +85 -0
  107. package/src/common/modal/stories/mock.tsx +46 -0
  108. package/src/common/modal/types.ts +56 -0
  109. package/src/common/nav-tab/NavDrawer.tsx +34 -0
  110. package/src/common/nav-tab/NavTitles.tsx +52 -0
  111. package/src/common/nav-tab/NsNavTab.spec.tsx +4 -5
  112. package/src/common/nav-tab/NsNavTab.stories.tsx +3 -3
  113. package/src/common/nav-tab/NsNavTab.styled.tsx +13 -7
  114. package/src/common/nav-tab/NsNavTab.tsx +21 -75
  115. package/src/common/nav-tab/index.ts +1 -1
  116. package/src/common/nav-tab/types.d.ts +3 -3
  117. package/src/common/progress/NsProgress.spec.tsx +47 -0
  118. package/src/common/progress/NsProgress.stories.tsx +37 -0
  119. package/src/common/progress/NsProgress.styled.tsx +39 -0
  120. package/src/common/progress/NsProgress.tsx +27 -0
  121. package/src/common/progress/index.ts +2 -0
  122. package/src/common/progress/types.d.ts +21 -0
  123. package/src/common/radiogroup/NsRadioGroup.spec.tsx +24 -25
  124. package/src/common/radiogroup/NsRadioGroup.stories.tsx +13 -31
  125. package/src/common/radiogroup/NsRadioGroup.styled.tsx +17 -15
  126. package/src/common/radiogroup/NsRadioGroup.tsx +6 -5
  127. package/src/common/search-bar/NsSearchBar.tsx +1 -3
  128. package/src/common/select/NsSelect.spec.tsx +107 -33
  129. package/src/common/select/NsSelect.styled.tsx +89 -39
  130. package/src/common/select/NsSelect.tsx +109 -44
  131. package/src/common/select/NsSelectOption.tsx +0 -1
  132. package/src/common/select/stories/NsSelect.stories.tsx +33 -24
  133. package/src/common/select/types.d.ts +6 -4
  134. package/src/common/spinner/NsSpinner.spec.tsx +21 -0
  135. package/src/common/spinner/NsSpinner.stories.tsx +26 -0
  136. package/src/common/spinner/NsSpinner.styled.tsx +344 -0
  137. package/src/common/spinner/NsSpinner.tsx +14 -0
  138. package/src/common/spinner/icons/Spinner.tsx +161 -0
  139. package/src/common/spinner/index.ts +2 -0
  140. package/src/common/spinner/types.ts +5 -0
  141. package/src/common/table/NsTable.tsx +32 -49
  142. package/src/common/table/NsTableHeader.tsx +30 -51
  143. package/src/common/util.ts +1 -0
  144. package/src/common/utility/Show.tsx +13 -0
  145. package/src/common/utility/index.ts +1 -0
  146. package/src/common/videos/types.d.ts +1 -1
  147. /package/src/common/brand-components/{NsBrandContanier.styled.tsx → NsBrandContainer.styled.tsx} +0 -0
@@ -3,7 +3,7 @@ import { CheckboxProps as MUICheckboxProps } from '@material-ui/core/Checkbox';
3
3
  import { NsFlagProps } from '../flag';
4
4
 
5
5
  // Define the props for the custom checkbox component
6
- export type NsCheckboxProps = Partial<Pick<MUICheckboxProps, 'label' | 'onChange'>> & {
6
+ export type NsCheckboxProps = Pick<MUICheckboxProps, 'label' | 'onChange'> & {
7
7
  name: string;
8
8
  className?: string;
9
9
  checked: boolean;
@@ -21,7 +21,7 @@ export const commonStyles = {
21
21
  ['1','2'] => ['27%','53%']
22
22
  */
23
23
  export function getPercentArray(fractionArray?: string[] | number[]) {
24
- let numbers = fractionArray?.map((numberString) => +numberString)?.filter((num) => 0 < num && num < 10) ?? [];
24
+ const numbers = fractionArray?.map((numberString) => +numberString)?.filter((num) => 0 < num && num < 10) ?? [];
25
25
  const sum = numbers.reduce((currentSum, currentNumber) => currentSum + currentNumber, 0);
26
26
  return numbers.map((currentNumber) => ((currentNumber / sum) * 80).toFixed(2) + '%');
27
27
  }
@@ -40,15 +40,15 @@ const sizeKeyMap: Record<ValueOf<typeof sizeKeyPrefix>, string> = {
40
40
  * @returns {fractions:['1','1','1'], tabletFractions:['1','1'], mobileFractions:['1'] }
41
41
  */
42
42
  export const getColumnSizes = (inputColumnClass: string): sizeObjectType => {
43
- let columnClass = inputColumnClass.trim() as NsColumnControlProps['columnClass'];
43
+ const columnClass = inputColumnClass.trim();
44
44
  const validClasses = columnClass
45
45
  .split(' ')
46
46
  .filter((e) => Object.values(sizeKeyPrefix).some((prefix) => e.startsWith(prefix)));
47
47
  const sizeObject: sizeObjectType = validClasses.reduce((acc: any, current: string) => {
48
- for (let key of Object.values(sizeKeyPrefix)) {
48
+ for (const key of Object.values(sizeKeyPrefix)) {
49
49
  if (current.startsWith(key)) {
50
- let interClass = current.replace(key, '');
51
- let sizes = interClass.split('-').filter((e) => /^[1-9]$/.test(e));
50
+ const interClass = current.replace(key, '');
51
+ const sizes = interClass.split('-').filter((e) => /^[1-9]$/.test(e));
52
52
  acc[sizeKeyMap[key]] = sizes;
53
53
  break;
54
54
  }
@@ -1,12 +1,21 @@
1
- export type ContentStackLiveTags = {
2
- 'data-cslp'?: string;
3
- };
1
+ import { FoundationTheme } from '@nuskin/foundation-theme';
2
+
3
+ export type CslpTag = { 'data-cslp': string };
4
4
 
5
5
  export type AdditionalProps = {
6
- darkModeEnabled?: boolean;
7
6
  additionalProps?: {
8
7
  ariaLabel?: string;
9
8
  testid?: string;
10
9
  className?: string;
11
- } & ContentStackLiveTags;
10
+ 'data-testid'?: string;
11
+ } & Partial<CslpTag>;
12
+ cslp?: Record<string, CslpTag>;
13
+ } & DarkModeEnabledOption;
14
+
15
+ export type Themed = {
16
+ theme: FoundationTheme.Theme;
17
+ };
18
+
19
+ export type DarkModeEnabledOption = {
20
+ darkModeEnabled?: boolean;
12
21
  };
@@ -1,5 +1,6 @@
1
1
  import { styled, ColorUtils, FoundationTheme } from '@nuskin/foundation-theme';
2
- const { getPaletteColor, getAppColor } = ColorUtils;
2
+ import { Themed } from '../common.types';
3
+ const { color, paletteColor, isDarkMode } = ColorUtils;
3
4
 
4
5
  export const DropdownContainer = styled.div<{ disabled: boolean }>`
5
6
  position: relative;
@@ -7,15 +8,22 @@ export const DropdownContainer = styled.div<{ disabled: boolean }>`
7
8
  opacity: ${({ disabled }) => (disabled ? '0.5' : '1')};
8
9
  `;
9
10
 
11
+ /** this function also used for SelectHead */
12
+ export const getDropdownHeadBorder = ({ type, theme }: { type: string } & Themed) => {
13
+ if (type === 'outlined') {
14
+ const borderColor = color('primary', 'black')({ theme });
15
+ return `1px solid ${borderColor}`;
16
+ }
17
+ return 'none';
18
+ };
10
19
  export const DropdownHead = styled.div<{ type: string; disabled: boolean }>`
11
20
  cursor: ${({ disabled }) => (disabled ? 'inherit' : 'pointer')};
12
21
  padding: 5px 10px;
13
22
  display: flex;
14
23
  justify-content: space-between;
15
24
  align-items: center;
16
- border: ${({ type, theme }) =>
17
- type === 'outlined' ? `1px solid ${getAppColor(theme, 'primary', 'black')}` : 'none'};
18
- color: ${({ theme }) => getPaletteColor(theme, 'primary')};
25
+ border: ${getDropdownHeadBorder}
26
+ color: ${paletteColor('primary')};
19
27
  `;
20
28
 
21
29
  export const DropdownButton = styled.div`
@@ -25,27 +33,19 @@ export const DropdownButton = styled.div`
25
33
  export const DropdownList = styled.div<{ borderColor: FoundationTheme.PaletteColors }>`
26
34
  position: absolute;
27
35
  z-index: 100;
28
- border: 1px solid ${({ borderColor, theme }) => getPaletteColor(theme, borderColor)};
36
+ border: 1px solid ${({ borderColor, theme }) => paletteColor(borderColor)({ theme })};
29
37
  padding: 0;
30
38
  margin: 0;
31
39
  width: 100%;
32
- color: ${({ theme }) => getPaletteColor(theme, 'primary', 'main')};
33
- background-color: ${({ theme }) => getPaletteColor(theme, 'primary', 'contrastText')};
40
+ color: ${paletteColor('primary', 'main')};
41
+ background-color: ${paletteColor('primary', 'contrastText')};
34
42
  `;
35
- const getIsLight = (theme: FoundationTheme.Theme) => {
36
- const { palette = { primary: { main: '', contrastText: '' } } } = theme;
37
- return palette.primary.main === getAppColor(theme, 'primary', 'black');
38
- };
39
43
 
40
44
  const getDividerColor = (theme: FoundationTheme.Theme) => {
41
- const isLight = getIsLight(theme);
42
- const dividerColor = isLight ? getAppColor(theme, 'neutral', '30') : getAppColor(theme, 'neutral', '20');
43
- return dividerColor;
45
+ return color('neutral', isDarkMode({ theme }) ? '20' : '30')({ theme });
44
46
  };
45
47
  const getListBgColor = (theme: FoundationTheme.Theme) => {
46
- const isLight = getIsLight(theme);
47
- const bgColor = isLight ? getAppColor(theme, 'accentColorB', '10') : getAppColor(theme, 'accentColorB', '50');
48
- return bgColor;
48
+ return color('accentColorB', isDarkMode({ theme }) ? '50' : '10')({ theme });
49
49
  };
50
50
  export const ListItemContainer = styled.div<{ showListDivider: boolean }>`
51
51
  cursor: pointer;
@@ -61,7 +61,12 @@ export const ListItemContainer = styled.div<{ showListDivider: boolean }>`
61
61
  }
62
62
  `;
63
63
 
64
+ const getBorder = ({ isSelected, theme }: { isSelected: boolean } & Themed) => {
65
+ const borderColor = paletteColor('primary')({ theme });
66
+ return isSelected ? `2px solid ${borderColor}` : 'none';
67
+ };
68
+
64
69
  export const ListItem = styled.span<{ isSelected: boolean }>`
65
70
  padding: 5px 10px;
66
- border-left: ${({ isSelected, theme }) => (isSelected ? `2px solid ${getPaletteColor(theme, 'primary')}` : 'none')};
71
+ border-left: ${getBorder};
67
72
  `;
@@ -1,7 +1,9 @@
1
- import React, { useState } from 'react';
1
+ import React, { useRef, useState } from 'react';
2
+
2
3
  import { DropdownContainer, DropdownButton, DropdownList, DropdownHead } from './NsDropdown.styled';
3
4
  import { NsIcon } from '../icon';
4
5
  import { DropdownProps } from './types.d';
6
+ import { useOnClickOutside } from '../hooks/useOnClickOutside';
5
7
 
6
8
  const NsDropdown: React.FC<DropdownProps> = ({
7
9
  selectedValue,
@@ -17,12 +19,15 @@ const NsDropdown: React.FC<DropdownProps> = ({
17
19
  }) => {
18
20
  const [isOpen, setIsOpen] = useState(false);
19
21
  const [value, setValue] = useState(selectedValue || '');
22
+ const dropdowneRef = useRef<HTMLDivElement | null>(null);
20
23
 
21
24
  const handleToggle = () => {
22
25
  if (disabled) return;
23
26
  setIsOpen(!isOpen);
24
27
  };
25
28
 
29
+ useOnClickOutside(dropdowneRef, handleToggle);
30
+
26
31
  const closeDropdown = () => {
27
32
  if (closeOnMouseLeave) {
28
33
  setIsOpen(false);
@@ -36,7 +41,7 @@ const NsDropdown: React.FC<DropdownProps> = ({
36
41
  };
37
42
 
38
43
  return (
39
- <DropdownContainer disabled={disabled}>
44
+ <DropdownContainer disabled={disabled} ref={dropdowneRef}>
40
45
  <DropdownHead data-testid="dropdown-head" disabled={disabled} type={type} onClick={handleToggle}>
41
46
  <DropdownButton>{selectedLabel || placeholder}</DropdownButton>
42
47
  <NsIcon testId="chevron-icon" name={isOpen ? 'arrowUp' : 'arrowDown'} color="primary" />
@@ -0,0 +1,2 @@
1
+ export {default as useWindowSize} from './useWindowSize';
2
+ export {default as useAccordionMap} from './useAccordionMap';
@@ -0,0 +1,19 @@
1
+ import { renderHook, act } from '@testing-library/react-hooks';
2
+ import useWindowSize from '../useWindowSize';
3
+
4
+ describe('useWindowSize', () => {
5
+ it('should return the window size', () => {
6
+ const { result } = renderHook(useWindowSize);
7
+ expect(result.current).toEqual({ width: window.innerWidth, height: window.innerHeight });
8
+ });
9
+
10
+ it('should update the window size when the window is resized', () => {
11
+ const { result } = renderHook(() => useWindowSize());
12
+ act(() => {
13
+ window.innerWidth = 500;
14
+ window.innerHeight = 500;
15
+ window.dispatchEvent(new Event('resize'));
16
+ });
17
+ expect(result.current).toEqual({ width: 500, height: 500 });
18
+ });
19
+ });
@@ -0,0 +1,39 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+
3
+ type ItemMap = Record<string, boolean>;
4
+ type UseAccordionProp = {
5
+ allowMultipleExpanded?: boolean;
6
+ defaultOpenIds?: string[];
7
+ };
8
+ const reducer = (map: ItemMap, id: string) => {
9
+ map[id] = true;
10
+ return map;
11
+ };
12
+
13
+ const useAccordionMap = ({ allowMultipleExpanded, defaultOpenIds }: UseAccordionProp) => {
14
+ const mapSetter = useCallback(() => {
15
+ let ids = defaultOpenIds;
16
+ if (!allowMultipleExpanded) {
17
+ ids = ids?.slice(0, 1) ?? [];
18
+ }
19
+ return ids?.reduce(reducer, {}) ?? {};
20
+ }, [defaultOpenIds, allowMultipleExpanded]);
21
+
22
+ const [itemStateMap, setItemMap] = useState<ItemMap>(mapSetter);
23
+
24
+ const toggleOpen = (index: string | number) => {
25
+ const key = index + '';
26
+ if (allowMultipleExpanded) {
27
+ setItemMap((prevState) => ({ ...prevState, [key]: !prevState[key] }));
28
+ } else {
29
+ setItemMap({ [key]: !itemStateMap[index] });
30
+ }
31
+ };
32
+
33
+ useEffect(() => {
34
+ setItemMap(mapSetter);
35
+ }, [mapSetter]);
36
+ return { itemStateMap, toggleOpen };
37
+ };
38
+
39
+ export default useAccordionMap;
@@ -0,0 +1,25 @@
1
+ import { RefObject, useEffect } from 'react';
2
+
3
+ export type Handler = () => void;
4
+
5
+ export function useOnClickOutside<T extends HTMLElement = HTMLElement>(
6
+ ref: RefObject<T>,
7
+ handler: Handler,
8
+ mouseEvent: 'click' | 'mousedown' | 'mouseup' = 'click'
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
+ useEffect(() => {
18
+ if (document) {
19
+ document.addEventListener(mouseEvent, handleEventListener);
20
+ return () => {
21
+ document.removeEventListener(mouseEvent, handleEventListener);
22
+ };
23
+ }
24
+ }, []);
25
+ }
@@ -0,0 +1,30 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ const useWindowSize = () => {
4
+ const [windowSize, setWindowSize] = useState({
5
+ width: window.innerWidth,
6
+ height: window.innerHeight,
7
+ });
8
+
9
+ useEffect(() => {
10
+ function handleResize() {
11
+ setWindowSize({
12
+ width: window?.innerWidth || 300,
13
+ height: window?.innerHeight || 300,
14
+ });
15
+ }
16
+ if (typeof window !== 'undefined') {
17
+ window.addEventListener('resize', handleResize);
18
+ }
19
+
20
+ return () => {
21
+ if (typeof window !== 'undefined') {
22
+ window.removeEventListener('resize', handleResize);
23
+ }
24
+ };
25
+ }, []);
26
+
27
+ return windowSize;
28
+ };
29
+
30
+ export default useWindowSize;
@@ -7,7 +7,7 @@ describe('NsIcon Component', () => {
7
7
  const { getByTestId } = render(<NsIcon name="cart" size="medium" color="secondary" />);
8
8
  const icon = getByTestId('icon');
9
9
  const svgIcon = icon.querySelector('svg');
10
- expect(svgIcon).toHaveClass('MuiSvgIcon-colorSecondary');
10
+ // expect(svgIcon).toHaveClass('MuiSvgIcon-colorSecondary');
11
11
  expect(svgIcon).toHaveClass('MuiSvgIcon-fontSizeMedium');
12
12
  });
13
13
 
@@ -20,6 +20,9 @@ export default {
20
20
  control: 'select',
21
21
  options: ['primary', 'secondary', 'success', 'error', 'warning', 'info'],
22
22
  },
23
+ colorOverride: {
24
+ control: 'color',
25
+ },
23
26
  },
24
27
  args: {
25
28
  size: 'large',
@@ -1,7 +1,10 @@
1
+ import { useTheme } from '@mui/material';
2
+ import { ColorUtils } from '@nuskin/foundation-theme';
1
3
  import { IconContainer } from './NsIcon.styled';
2
4
  import IconVariants from './NsIconVariants';
3
5
  import { NsIconProps } from './types.d';
4
6
 
7
+ const { paletteColor } = ColorUtils;
5
8
  const NsIcon = ({
6
9
  name,
7
10
  size = 'large',
@@ -11,14 +14,15 @@ const NsIcon = ({
11
14
  colorOverride,
12
15
  }: NsIconProps) => {
13
16
  const Icon = IconVariants[name];
14
-
17
+ const theme = useTheme();
18
+ const iconColor = paletteColor(color)({ theme });
15
19
  if (!Icon) {
16
20
  return null;
17
21
  }
18
22
 
19
23
  return (
20
24
  <IconContainer data-testid={testId} size={size} className={className}>
21
- <Icon color={colorOverride ? colorOverride : (color as any)} />
25
+ <Icon color={colorOverride ? colorOverride : (iconColor as any)} />
22
26
  </IconContainer>
23
27
  );
24
28
  };
@@ -19,6 +19,7 @@ import WarningIcon from './icons/WarningIcon';
19
19
  import CloseIcon from './icons/CloseIcon';
20
20
  import UserIcon from './icons/UserIcon';
21
21
  import HamburgerIcon from './icons/HamburgerIcon';
22
+ import UserOutline from './icons/UserOutline';
22
23
 
23
24
  const IconVariants = {
24
25
  arrowDown: ArrowDown,
@@ -41,7 +42,9 @@ const IconVariants = {
41
42
  slash: SlashIcon,
42
43
  trash: TrashIcon,
43
44
  userIcon: UserIcon,
45
+ userOutline: UserOutline,
44
46
  warning: WarningIcon,
45
47
  };
46
48
 
47
49
  export default IconVariants;
50
+ export const isValidIconName = (name: string) => Object.keys(IconVariants).includes(name);
@@ -24,7 +24,7 @@ const AlertErrorIcon = (props: SvgIconProps) => {
24
24
  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
25
  15.2093943 18.0314841,15.0216954 17.9465423,14.8670135 Z"
26
26
  id="Shape"
27
- fill={props.fill || '#B2140A'}
27
+ fill={props.color || '#B2140A'}
28
28
  fillRule="nonzero"
29
29
  ></path>
30
30
  <path
@@ -33,7 +33,7 @@ const AlertErrorIcon = (props: SvgIconProps) => {
33
33
  13.415 C9.35312473,13.5641498 9.1489517,13.6425377 8.94,13.63 C8.72997224,13.6424095 8.52401376,13.5683367 8.37,13.425 C8.23531653,13.2613056 8.17063593,
34
34
  13.0510936 8.19,12.84 Z M9.415,11.195 L8.5,11.195 L8.25,5.96 L9.675,5.96 L9.415,11.195 Z"
35
35
  id="Shape"
36
- fill={props.fill || '#FFFFFF'}
36
+ fill={'#FFFFFF'}
37
37
  fillRule="nonzero"
38
38
  ></path>
39
39
  </g>
@@ -1,28 +1,25 @@
1
- import { useTheme } from '@mui/material/styles';
2
1
  import { SvgIconProps, SvgIcon } from '@mui/material';
3
2
 
4
3
  const ArrowLeft = (props: SvgIconProps) => {
5
- const { palette } = useTheme();
6
- const strokeColor = palette.primary.main;
7
4
  return (
8
5
  <SvgIcon
9
- {...props}
10
6
  data-testid="arrowLeft"
11
7
  viewBox="0 0 24 24"
12
8
  preserveAspectRatio="xMidYMid meet"
13
9
  focusable="false"
14
10
  style={{ pointerEvents: 'none', display: 'block' }}
11
+ {...props}
15
12
  >
16
13
  <g
17
14
  id="icon-narrow-angle-left-24"
18
- stroke="none"
15
+ stroke={props.color}
19
16
  strokeWidth="1"
20
17
  fill="none"
21
18
  fillRule="evenodd"
22
19
  strokeLinecap="round"
23
20
  strokeLinejoin="round"
24
21
  >
25
- <g transform="translate(-196, -226)" stroke={strokeColor}>
22
+ <g transform="translate(-196, -226)" stroke={props.color || '#000'}>
26
23
  <polyline
27
24
  // eslint-disable-next-line max-len
28
25
  transform="translate(209.000000, 237.500000) scale(-1.2, 1.2) rotate(90.000000) translate(-209.000000, -237.500000)"
@@ -1,11 +1,9 @@
1
- import { useTheme } from '@mui/material/styles';
2
-
3
- export const ArrowRight = () => {
4
- const { palette } = useTheme();
5
- const strokeColor = palette.primary.main;
1
+ import { SvgIconProps, SvgIcon } from '@mui/material';
6
2
 
3
+ export const ArrowRight = (props: SvgIconProps) => {
7
4
  return (
8
- <svg
5
+ <SvgIcon
6
+ {...props}
9
7
  data-testid="arrowRight"
10
8
  viewBox="0 0 24 24"
11
9
  preserveAspectRatio="xMidYMid meet"
@@ -21,7 +19,7 @@ export const ArrowRight = () => {
21
19
  strokeLinecap="round"
22
20
  strokeLinejoin="round"
23
21
  >
24
- <g transform="translate(-196, -226)" stroke={strokeColor}>
22
+ <g transform="translate(-196, -226)" stroke={props.color || '#000'}>
25
23
  <polyline
26
24
  // eslint-disable-next-line max-len
27
25
  transform="translate(209.000000, 237.500000) scale(-1.2, 1.2) rotate(-90.000000) translate(-209.000000, -237.500000)"
@@ -29,7 +27,7 @@ export const ArrowRight = () => {
29
27
  ></polyline>
30
28
  </g>
31
29
  </g>
32
- </svg>
30
+ </SvgIcon>
33
31
  );
34
32
  };
35
33
 
@@ -4,38 +4,13 @@ import { SvgIconProps, SvgIcon } from '@mui/material';
4
4
 
5
5
  const Cart = (props: SvgIconProps) => {
6
6
  return (
7
- <SvgIcon
8
- viewBox="0 0 20 20"
9
- preserveAspectRatio="xMidYMid meet"
10
- focusable="false"
11
- {...props}
12
- data-testid="cart"
13
- >
14
- <g
15
- id="cart-icon"
16
- fill="none"
17
- strokeWidth="1"
18
- stroke="currentColor"
19
- transform="translate(-53.000000, -15.000000)"
20
- >
21
- <g id="Group">
22
- <g id="cart-icon" transform="translate(49.000000, 8.000000)">
23
- <g id="Asset-9Cart" transform="translate(4.000000, 8.000000)">
24
- <circle id="Oval" cx="7" cy="14.5" r="1"></circle>
25
- <circle id="Oval" cx="15" cy="14.5" r="1"></circle>
26
- <polyline
27
- id="Shape"
28
- strokeLinecap="round"
29
- points="5.68434189e-14 0 3 0 6 12 16 12"
30
- ></polyline>
31
- <path
32
- d="M5.5,9 C16.0765368,9 17.2536948,8.11206806 17.6216676,6.82416338 L19,2 L3.5,2"
33
- id="Shape"
34
- ></path>
35
- </g>
36
- </g>
37
- </g>
38
- </g>
7
+ <SvgIcon viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" data-testid="cart" {...props}>
8
+ <path
9
+ fillRule="evenodd"
10
+ clipRule="evenodd"
11
+ d="M0 3.79999C0 3.2477 0.447715 2.79999 1 2.79999H3.19159C4.13466 2.79999 4.94959 3.45879 5.14719 4.38093L5.237 4.80005H21C21.3079 4.80005 21.5987 4.94192 21.7882 5.18464C21.9777 5.42735 22.0448 5.74384 21.9701 6.04258L20.5382 11.7702C20.0931 13.5509 18.4931 14.8 16.6577 14.8H7.3798L7.59407 15.8H19C19.5523 15.8 20 16.2478 20 16.8C20 17.3523 19.5523 17.8 19 17.8H18H7.5H6.78564C6.3141 17.8 5.90664 17.4706 5.80784 17.0096L3.45079 6.00958L3.19159 4.79999H1C0.447715 4.79999 0 4.35227 0 3.79999ZM7.5 17.8C6.39543 17.8 5.5 18.6955 5.5 19.8C5.5 20.9046 6.39543 21.8 7.5 21.8C8.60457 21.8 9.5 20.9046 9.5 19.8C9.5 18.6955 8.60457 17.8 7.5 17.8ZM18 17.8C16.8954 17.8 16 18.6955 16 19.8C16 20.9046 16.8954 21.8 18 21.8C19.1046 21.8 20 20.9046 20 19.8C20 18.6955 19.1046 17.8 18 17.8ZM6.95124 12.8H16.6577C17.5754 12.8 18.3754 12.1755 18.598 11.2851L19.7192 6.80005H5.66557L6.95124 12.8Z"
12
+ fill={props.color}
13
+ />
39
14
  </SvgIcon>
40
15
  );
41
16
  };
@@ -8,8 +8,8 @@ const CartBadge = (props: SvgIconProps) => {
8
8
  viewBox="0 0 30 30"
9
9
  preserveAspectRatio="xMidYMid meet"
10
10
  focusable="false"
11
- {...props}
12
11
  data-testid="cartBadge"
12
+ {...props}
13
13
  >
14
14
  <g
15
15
  id="cart-icon-badge"
@@ -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="currentColor">
23
+ <g id="Group" transform="translate(0.000000, 0.500000)" stroke={props.color || 'currentColor'}>
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
@@ -12,7 +12,7 @@ const CloseIcon = (props: SvgIconProps) => {
12
12
  {...props}
13
13
  data-testid="close"
14
14
  >
15
- <g fill="none" stroke="#000000" strokeWidth="1">
15
+ <g fill="none" stroke={props.color || '#000'} strokeWidth="1">
16
16
  <path strokeLinecap="round" d="M 0 0 L 12 12 M 0 12 L 12 0" />
17
17
  </g>
18
18
  </SvgIcon>
@@ -2,7 +2,7 @@
2
2
  import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon';
3
3
  /**
4
4
  *
5
- * @param props { fill: color to be filled for icon, color: color of the tick icon}
5
+ * @param props { color: color to be filled for icon}
6
6
  * @returns ErrorIcon, can be used in Input field, when input field is in error state
7
7
  */
8
8
  const ErrorIcon = (props: SvgIconProps) => {
@@ -18,7 +18,7 @@ const ErrorIcon = (props: SvgIconProps) => {
18
18
  >
19
19
  <path
20
20
  d="M17.07 2.92999C15.4338 1.29429 13.2811 0.276523 10.9786 0.0501185C8.67611 -0.176286 6.36637 0.402675 4.44291 1.68835C2.51945 2.97403 1.1013 4.88687 0.430089 7.10094C-0.241121 9.31502 -0.123857 11.6933 0.761901 13.8306C1.64766 15.9679 3.24711 17.732 5.28771 18.8222C7.32831 19.9125 9.68381 20.2614 11.9528 19.8096C14.2219 19.3578 16.2641 18.1332 17.7314 16.3445C19.1988 14.5557 20.0005 12.3136 20 9.99999C19.9994 7.34804 18.9455 4.80491 17.07 2.92999ZM9 5.37999C9 5.11477 9.10536 4.86041 9.29289 4.67288C9.48043 4.48534 9.73478 4.37999 10 4.37999C10.2652 4.37999 10.5196 4.48534 10.7071 4.67288C10.8946 4.86041 11 5.11477 11 5.37999V10.6C11 10.8652 10.8946 11.1196 10.7071 11.3071C10.5196 11.4946 10.2652 11.6 10 11.6C9.73478 11.6 9.48043 11.4946 9.29289 11.3071C9.10536 11.1196 9 10.8652 9 10.6V5.37999ZM10 15.51C9.74684 15.51 9.49936 15.4349 9.28887 15.2943C9.07837 15.1536 8.91431 14.9537 8.81743 14.7198C8.72055 14.4859 8.6952 14.2286 8.74459 13.9803C8.79398 13.732 8.91589 13.5039 9.0949 13.3249C9.27391 13.1459 9.50199 13.024 9.75028 12.9746C9.99858 12.9252 10.2559 12.9505 10.4898 13.0474C10.7237 13.1443 10.9236 13.3084 11.0643 13.5189C11.2049 13.7294 11.28 13.9768 11.28 14.23C11.28 14.5695 11.1451 14.895 10.9051 15.1351C10.665 15.3751 10.3395 15.51 10 15.51Z"
21
- fill={props.fill || '#C81D1D'}
21
+ fill={props.color || '#C81D1D'}
22
22
  />
23
23
  </SvgIcon>
24
24
  );
@@ -2,22 +2,25 @@
2
2
  import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon';
3
3
  /**
4
4
  *
5
- * @param props {fill: color to be filled for icon, color: color of the tick icon}
5
+ * @param props {stroke}
6
6
  */
7
7
  const HamburgerIcon = (props: SvgIconProps) => {
8
8
  return (
9
- <SvgIcon viewBox="0 0 24 24" {...props} data-testid="hamburgerIcon" xmlns="http://www.w3.org/2000/svg">
9
+ <SvgIcon viewBox="0 0 24 18" {...props} data-testid="hamburgerIcon" xmlns="http://www.w3.org/2000/svg">
10
10
  <path
11
+ stroke={props.color}
11
12
  fillRule="evenodd"
12
13
  clipRule="evenodd"
13
14
  d="M0 1C0 0.447715 0.447715 0 1 0H19C19.5523 0 20 0.447715 20 1C20 1.55228 19.5523 2 19 2H1C0.447715 2 0 1.55228 0 1Z"
14
15
  />
15
16
  <path
17
+ stroke={props.color}
16
18
  fillRule="evenodd"
17
19
  clipRule="evenodd"
18
20
  d="M0 9C0 8.44772 0.447715 8 1 8H19C19.5523 8 20 8.44772 20 9C20 9.55228 19.5523 10 19 10H1C0.447715 10 0 9.55228 0 9Z"
19
21
  />
20
22
  <path
23
+ stroke={props.color}
21
24
  fillRule="evenodd"
22
25
  clipRule="evenodd"
23
26
  d="M0 17C0 16.4477 0.447715 16 1 16H19C19.5523 16 20 16.4477 20 17C20 17.5523 19.5523 18 19 18H1C0.447715 18 0 17.5523 0 17Z"
@@ -20,7 +20,7 @@ const SearchIcon = (props: SvgIconProps) => {
20
20
  fillRule="evenodd"
21
21
  clipRule="evenodd"
22
22
  d="M0 9.98777C0 4.47167 4.46598 0 9.97505 0C15.4841 0 19.9501 4.47167 19.9501 9.98777C19.9501 11.7568 19.49 13.4205 18.6829 14.8629L23.2366 19.4844C24.2593 20.5223 24.2537 22.1921 23.2241 23.2231C22.1755 24.273 20.4704 24.2567 19.442 23.1869L15.0315 18.5988C13.5485 19.4736 11.8192 19.9755 9.97505 19.9755C4.46598 19.9755 0 15.5039 0 9.98777ZM16.6388 17.42L20.8646 21.8159C21.1265 22.0884 21.5608 22.0926 21.8279 21.8251C22.0902 21.5625 22.0916 21.1372 21.8311 20.8728L17.5309 16.5087C17.2531 16.8311 16.9551 17.1355 16.6388 17.42ZM9.97505 1.97696C5.55644 1.97696 1.97444 5.56352 1.97444 9.98777C1.97444 14.412 5.55644 17.9986 9.97505 17.9986C11.7025 17.9986 13.2995 17.4515 14.6063 16.5208C15.3948 15.9594 16.0773 15.2583 16.6179 14.454C17.4753 13.1784 17.9757 11.6431 17.9757 9.98777C17.9757 5.56352 14.3937 1.97696 9.97505 1.97696Z"
23
- fill="#5F5F5F"
23
+ fill={props.color || '#5F5F5F'}
24
24
  />
25
25
  </SvgIcon>
26
26
  );
@@ -1,10 +1,5 @@
1
1
  /* eslint-disable max-len */
2
2
  import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon';
3
- /**
4
- *
5
- * @param props {fill: color to be filled for icon, color: color of the tick icon}
6
- * @returns SuccessIcon, can be used in Input field, when input field is in success state
7
- */
8
3
  const SuccessIcon = (props: SvgIconProps) => {
9
4
  return (
10
5
  <SvgIcon
@@ -17,7 +12,7 @@ const SuccessIcon = (props: SvgIconProps) => {
17
12
  {...props}
18
13
  >
19
14
  <g id="Check Circle">
20
- <circle id="Ellipse 70" cx="10" cy="10" r="10" fill={props.fill || '#5BAC85'} />
15
+ <circle id="Ellipse 70" cx="10" cy="10" r="10" fill={props.color || '#5BAC85'} />
21
16
  <g id="check 12px">
22
17
  <g id="checkmark">
23
18
  <path
@@ -25,7 +20,7 @@ const SuccessIcon = (props: SvgIconProps) => {
25
20
  fillRule="evenodd"
26
21
  clipRule="evenodd"
27
22
  d="M15.8051 5.79289C16.1956 6.18342 16.1956 6.81658 15.8051 7.20711L8.80508 14.2071C8.61742 14.3948 8.36287 14.5001 8.09748 14.5C7.8321 14.4999 7.57765 14.3943 7.39018 14.2064L3.79218 10.6014C3.40203 10.2105 3.40265 9.57735 3.79355 9.18721C4.18445 8.79706 4.81762 8.79768 5.20776 9.18858L8.09866 12.0851L14.3909 5.79289C14.7814 5.40237 15.4146 5.40237 15.8051 5.79289Z"
28
- fill={props.color || 'white'}
23
+ fill={props.fill || '#fff'}
29
24
  />
30
25
  </g>
31
26
  </g>
@@ -20,7 +20,7 @@ const TrashIcon = (props: SvgIconProps) => {
20
20
  fillRule="evenodd"
21
21
  clipRule="evenodd"
22
22
  d="M10 0C10.5523 0 11 0.447715 11 1V2.5H19C19.5523 2.5 20 2.94772 20 3.5C20 4.05228 19.5523 4.5 19 4.5H17.5V18C17.5 19.6569 16.1569 21 14.5 21H5.5C3.84315 21 2.5 19.6569 2.5 18V4.5H1C0.447715 4.5 0 4.05228 0 3.5C0 2.94772 0.447715 2.5 1 2.5H9V1C9 0.447715 9.44771 0 10 0ZM4.5 4.5V18C4.5 18.5523 4.94772 19 5.5 19H14.5C15.0523 19 15.5 18.5523 15.5 18V4.5H4.5ZM6.5 5.5C7.05228 5.5 7.5 5.94772 7.5 6.5V17C7.5 17.5523 7.05228 18 6.5 18C5.94772 18 5.5 17.5523 5.5 17V6.5C5.5 5.94772 5.94772 5.5 6.5 5.5ZM10 5.5C10.5523 5.5 11 5.94772 11 6.5V17C11 17.5523 10.5523 18 10 18C9.44771 18 9 17.5523 9 17V6.5C9 5.94772 9.44771 5.5 10 5.5ZM13.5 5.5C14.0523 5.5 14.5 5.94772 14.5 6.5V17C14.5 17.5523 14.0523 18 13.5 18C12.9477 18 12.5 17.5523 12.5 17V6.5C12.5 5.94772 12.9477 5.5 13.5 5.5Z"
23
- fill={props.fill || '#252525'}
23
+ fill={props.color || '#252525'}
24
24
  />
25
25
  </SvgIcon>
26
26
  );