@nuskin/foundation-ui-components 1.0.0-cft-35.13 → 1.0.0-develop.10

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 (247) hide show
  1. package/package.json +21 -11
  2. package/src/common/accordion/NsAccordion.tsx +69 -0
  3. package/src/common/accordion/NsAccordionHeader.tsx +77 -0
  4. package/src/common/accordion/NsAccordionItem.tsx +59 -0
  5. package/src/common/accordion/NsCollapse.tsx +110 -0
  6. package/src/common/accordion/NsToggleSection.tsx +43 -0
  7. package/src/common/accordion/ToggleSection.styled.tsx +32 -0
  8. package/src/common/accordion/colorMap.ts +117 -0
  9. package/src/common/accordion/enums.ts +8 -0
  10. package/src/common/accordion/index.ts +7 -0
  11. package/src/common/accordion/specs/NsAccordion.spec.tsx +163 -0
  12. package/src/common/accordion/specs/NsAccordionHeader.spec.tsx +130 -0
  13. package/src/common/accordion/specs/NsAccordionItem.spec.tsx +79 -0
  14. package/src/common/accordion/specs/NsCollapse.spec.tsx +129 -0
  15. package/src/common/accordion/specs/NsToggleSection.spec.tsx +74 -0
  16. package/src/common/accordion/stories/NsAccordion.stories.tsx +65 -0
  17. package/src/common/accordion/stories/NsAccordionHeader.stories.tsx +44 -0
  18. package/src/common/accordion/stories/NsAccordionItem.stories.tsx +45 -0
  19. package/src/common/accordion/stories/NsCollapse.stories.tsx +50 -0
  20. package/src/common/accordion/stories/NsToggleSection.stories.tsx +26 -0
  21. package/src/common/accordion/stories/data.tsx +143 -0
  22. package/src/common/accordion/types.d.ts +81 -0
  23. package/src/common/alert/Header.tsx +29 -0
  24. package/src/common/alert/NsAlert.spec.tsx +62 -29
  25. package/src/common/alert/NsAlert.stories.tsx +18 -17
  26. package/src/common/alert/NsAlert.styled.tsx +124 -0
  27. package/src/common/alert/NsAlert.tsx +27 -110
  28. package/src/common/alert/index.ts +2 -2
  29. package/src/common/alert/types.d.ts +23 -23
  30. package/src/common/auth-dropdown/NsAuthDropdown.spec.tsx +24 -6
  31. package/src/common/auth-dropdown/NsAuthDropdown.stories.tsx +2 -2
  32. package/src/common/auth-dropdown/NsAuthDropdown.styled.ts +21 -16
  33. package/src/common/auth-dropdown/NsAuthDropdown.tsx +7 -6
  34. package/src/common/auth-dropdown/index.ts +1 -1
  35. package/src/common/auth-dropdown/types.d.ts +3 -1
  36. package/src/common/autocomplete/NsAutocomplete.spec.tsx +69 -24
  37. package/src/common/autocomplete/NsAutocomplete.stories.tsx +64 -7
  38. package/src/common/autocomplete/NsAutocomplete.styled.tsx +12 -0
  39. package/src/common/autocomplete/NsAutocomplete.tsx +100 -46
  40. package/src/common/autocomplete/index.ts +2 -1
  41. package/src/common/autocomplete/mock/MockData.ts +50 -7
  42. package/src/common/autocomplete/mock/MockUI.tsx +2 -1
  43. package/src/common/autocomplete/{types.ts → types.d.ts} +18 -8
  44. package/src/common/banner/NsBanner.spec.tsx +13 -4
  45. package/src/common/banner/NsBanner.stories.tsx +2 -2
  46. package/src/common/banner/NsBanner.styled.tsx +1 -1
  47. package/src/common/banner/NsBanner.tsx +1 -1
  48. package/src/common/banner/index.ts +1 -1
  49. package/src/common/brand-components/NsBrand.stories.tsx +2 -2
  50. package/src/common/brand-components/NsBrandComponent.tsx +6 -3
  51. package/src/common/brand-components/index.ts +1 -1
  52. package/src/common/breadcrumb/NsBreadcrumb.spec.tsx +8 -9
  53. package/src/common/breadcrumb/NsBreadcrumb.stories.tsx +3 -5
  54. package/src/common/breadcrumb/NsBreadcrumb.tsx +5 -3
  55. package/src/common/breadcrumb/data.ts +10 -0
  56. package/src/common/breadcrumb/index.ts +1 -1
  57. package/src/common/button/NsButton.spec.tsx +66 -51
  58. package/src/common/button/NsButton.stories.tsx +61 -4
  59. package/src/common/button/NsButton.styled.tsx +54 -72
  60. package/src/common/button/NsButton.tsx +9 -3
  61. package/src/common/button/helper.ts +82 -0
  62. package/src/common/button/index.ts +1 -1
  63. package/src/common/button/types.d.ts +3 -0
  64. package/src/common/card/NsFfgCard.tsx +14 -8
  65. package/src/common/card/specs/NsFfgCard.spec.tsx +33 -0
  66. package/src/common/card/stories/NsBgCard.stories.tsx +2 -4
  67. package/src/common/card/stories/NsBrandProductCard.stories.tsx +2 -2
  68. package/src/common/card/stories/NsFfgCard.stories.tsx +49 -16
  69. package/src/common/card/stories/{data.js → data.ts} +1 -25
  70. package/src/common/card/styled/NsFfgStyled.tsx +9 -7
  71. package/src/common/card/types.d.ts +7 -4
  72. package/src/common/carousal/NsScrollingCarousal.tsx +61 -0
  73. package/src/common/carousal/index.ts +3 -0
  74. package/src/common/carousal/{tests → specs}/NsCarousal.spec.tsx +2 -2
  75. package/src/common/carousal/specs/NsScrollingCarousal.spec.tsx +61 -0
  76. package/src/common/carousal/stories/NSCarousal.stories.tsx +2 -2
  77. package/src/common/carousal/stories/NsScrollingCarousal.stories.tsx +52 -0
  78. package/src/common/carousal/types.d.ts +23 -0
  79. package/src/common/cart-icon/NsCartIcon.spec.tsx +1 -1
  80. package/src/common/cart-icon/NsCartIcon.stories.tsx +2 -2
  81. package/src/common/cart-icon/NsCartIcon.tsx +1 -1
  82. package/src/common/cart-icon/index.ts +1 -1
  83. package/src/common/checkbox/NsCheckbox.spec.tsx +2 -2
  84. package/src/common/checkbox/NsCheckbox.stories.tsx +2 -2
  85. package/src/common/checkbox/NsCheckbox.styled.tsx +15 -16
  86. package/src/common/checkbox/NsCheckbox.tsx +2 -2
  87. package/src/common/checkbox/index.ts +1 -1
  88. package/src/common/checkbox/types.d.ts +1 -7
  89. package/src/common/column-control/NsColumnControl.tsx +1 -1
  90. package/src/common/column-control/NsFlexColumns.tsx +22 -0
  91. package/src/common/column-control/NsGridColumns.tsx +22 -0
  92. package/src/common/column-control/index.ts +3 -1
  93. package/src/common/column-control/{NsColumnControl.spec.tsx → specs/NsColumnControl.spec.tsx} +3 -3
  94. package/src/common/column-control/specs/NsFlexColumns.spec.tsx +24 -0
  95. package/src/common/column-control/specs/NsGridColumns.spec.tsx +24 -0
  96. package/src/common/column-control/stories/DivArray.stories.tsx +18 -0
  97. package/src/common/column-control/stories/DivArray.tsx +63 -0
  98. package/src/common/column-control/{NsColumnControl.stories.tsx → stories/NsColumnControl.stories.tsx} +9 -16
  99. package/src/common/column-control/stories/NsFlexColumns.stories.tsx +63 -0
  100. package/src/common/column-control/stories/NsGridColumns.stories.tsx +63 -0
  101. package/src/common/column-control/stories/common.tsx +14 -0
  102. package/src/common/column-control/styled/StyledFlex.tsx +45 -0
  103. package/src/common/column-control/styled/StyledGrid.tsx +40 -0
  104. package/src/common/column-control/styled/common.ts +27 -0
  105. package/src/common/column-control/types.d.ts +15 -16
  106. package/src/common/column-control/utils.ts +5 -5
  107. package/src/common/common.types.d.ts +21 -0
  108. package/src/common/dropdown/NsDropdown.spec.tsx +1 -1
  109. package/src/common/dropdown/NsDropdown.styled.tsx +23 -18
  110. package/src/common/dropdown/NsDropdown.tsx +1 -1
  111. package/src/common/dropdown/NsDropdownOption.tsx +1 -1
  112. package/src/common/dropdown/index.ts +1 -1
  113. package/src/common/dropdown/stories/NsDropdown.stories.tsx +2 -2
  114. package/src/common/dropdown/stories/NsDropdownCountry.stories.tsx +2 -2
  115. package/src/common/dropdown/types.d.ts +3 -1
  116. package/src/common/email-simple/NsEmailSimple.spec.tsx +145 -0
  117. package/src/common/email-simple/NsEmailSimple.stories.tsx +61 -0
  118. package/src/common/email-simple/NsEmailSimple.tsx +71 -0
  119. package/src/common/email-simple/index.ts +2 -0
  120. package/src/common/email-simple/types.d.ts +14 -0
  121. package/src/common/flag/NsFlag.spec.tsx +5 -25
  122. package/src/common/flag/NsFlag.stories.tsx +2 -2
  123. package/src/common/flag/NsFlag.styled.tsx +7 -9
  124. package/src/common/flag/NsFlag.tsx +1 -3
  125. package/src/common/flag/index.ts +1 -1
  126. package/src/common/flag/types.d.ts +0 -2
  127. package/src/common/hooks/index.ts +2 -0
  128. package/src/common/hooks/specs/useWindowSize.spec.tsx +19 -0
  129. package/src/common/hooks/useAccordionMap.tsx +39 -0
  130. package/src/common/hooks/useWindowSize.tsx +30 -0
  131. package/src/common/icon/NsIcon.spec.tsx +1 -1
  132. package/src/common/icon/NsIcon.stories.tsx +2 -2
  133. package/src/common/icon/NsIcon.styled.tsx +1 -1
  134. package/src/common/icon/NsIcon.tsx +1 -1
  135. package/src/common/icon/NsIconVariants.ts +9 -4
  136. package/src/common/icon/icons/AlertErrorIcon.tsx +15 -6
  137. package/src/common/icon/icons/HamburgerIcon.tsx +29 -0
  138. package/src/common/icon/icons/UserIcon.tsx +30 -0
  139. package/src/common/icon/icons/WarningIcon.tsx +14 -5
  140. package/src/common/icon/index.ts +1 -1
  141. package/src/common/icon/types.d.ts +1 -0
  142. package/src/common/image/NsImage.spec.tsx +57 -7
  143. package/src/common/image/NsImage.stories.tsx +2 -2
  144. package/src/common/image/NsImage.tsx +24 -12
  145. package/src/common/image/NsNextImage.spec.tsx +1 -1
  146. package/src/common/image/NsNextImage.stories.tsx +2 -2
  147. package/src/common/image/NsNextImage.tsx +1 -1
  148. package/src/common/image/index.ts +1 -1
  149. package/src/common/image/types.d.ts +1 -0
  150. package/src/common/index.ts +14 -10
  151. package/src/common/input/InputFieldWrapper.tsx +4 -4
  152. package/src/common/input/InputWrapper.tsx +12 -7
  153. package/src/common/input/NsInputField.stories.tsx +13 -10
  154. package/src/common/input/NsInputField.tsx +13 -6
  155. package/src/common/input/getAdornmentProps.tsx +5 -1
  156. package/src/common/input/index.ts +1 -1
  157. package/src/common/input/types.d.ts +18 -1
  158. package/src/common/label/NsLabel.stories.tsx +6 -1
  159. package/src/common/label/NsLabel.tsx +21 -20
  160. package/src/common/label/index.ts +1 -1
  161. package/src/common/label/types.d.ts +2 -1
  162. package/src/common/link/NsLink.spec.tsx +1 -1
  163. package/src/common/link/NsLink.stories.tsx +2 -2
  164. package/src/common/link/NsLink.tsx +1 -1
  165. package/src/common/link/index.ts +1 -1
  166. package/src/common/modal/NsModal.spec.tsx +46 -0
  167. package/src/common/modal/NsModal.styled.tsx +28 -0
  168. package/src/common/modal/NsModal.tsx +37 -0
  169. package/src/common/modal/helper-component/NsModalContentBox.tsx +32 -0
  170. package/src/common/modal/helper-component/NsModalDefault.tsx +25 -0
  171. package/src/common/modal/helper-component/NsModalStatus.tsx +15 -0
  172. package/src/common/modal/helper-component/NsModalStatusIcon.tsx +14 -0
  173. package/src/common/modal/helper-component/NsModalTitle.tsx +26 -0
  174. package/src/common/modal/index.ts +2 -0
  175. package/src/common/modal/model/NsModalVariantMap.tsx +24 -0
  176. package/src/common/modal/stories/NsModal.stories.tsx +86 -0
  177. package/src/common/modal/stories/mock.tsx +46 -0
  178. package/src/common/modal/types.ts +56 -0
  179. package/src/common/nav-product-card/NsNavProductCard.spec.tsx +1 -1
  180. package/src/common/nav-product-card/NsNavProductCard.stories.tsx +2 -2
  181. package/src/common/nav-product-card/NsNavProductCard.tsx +1 -1
  182. package/src/common/nav-product-card/index.ts +1 -1
  183. package/src/common/nav-tab/NsNavTab.spec.tsx +2 -7
  184. package/src/common/nav-tab/NsNavTab.stories.tsx +2 -2
  185. package/src/common/nav-tab/NsNavTab.styled.tsx +7 -4
  186. package/src/common/nav-tab/NsNavTab.tsx +1 -1
  187. package/src/common/nav-tab/index.ts +1 -1
  188. package/src/common/nav-tab/types.d.ts +2 -2
  189. package/src/common/radiogroup/NsRadioGroup.spec.tsx +63 -0
  190. package/src/common/radiogroup/NsRadioGroup.stories.tsx +84 -0
  191. package/src/common/radiogroup/NsRadioGroup.styled.tsx +52 -0
  192. package/src/common/radiogroup/NsRadioGroup.tsx +62 -0
  193. package/src/common/radiogroup/index.ts +2 -0
  194. package/src/common/radiogroup/types.d.ts +20 -0
  195. package/src/common/search-bar/NsSeachBar.spec.tsx +1 -1
  196. package/src/common/search-bar/NsSearchBar.tsx +1 -3
  197. package/src/common/select/NsSelect.spec.tsx +193 -0
  198. package/src/common/select/NsSelect.styled.tsx +104 -0
  199. package/src/common/select/NsSelect.tsx +117 -0
  200. package/src/common/select/NsSelectOption.tsx +30 -0
  201. package/src/common/select/index.ts +3 -0
  202. package/src/common/select/stories/NsSelect.stories.tsx +134 -0
  203. package/src/common/select/types.d.ts +34 -0
  204. package/src/common/spinner/NsSpinner.spec.tsx +21 -0
  205. package/src/common/spinner/NsSpinner.stories.tsx +26 -0
  206. package/src/common/spinner/NsSpinner.styled.tsx +344 -0
  207. package/src/common/spinner/NsSpinner.tsx +14 -0
  208. package/src/common/spinner/icons/Spinner.tsx +161 -0
  209. package/src/common/spinner/index.ts +2 -0
  210. package/src/common/spinner/types.ts +5 -0
  211. package/src/common/table/NsExpandableRow.tsx +68 -0
  212. package/src/common/table/NsTable.spec.tsx +174 -0
  213. package/src/common/table/NsTable.stories.tsx +161 -0
  214. package/src/common/table/NsTable.styled.ts +127 -0
  215. package/src/common/table/NsTable.tsx +133 -0
  216. package/src/common/table/NsTableHeader.tsx +41 -0
  217. package/src/common/table/NsTablePagination.tsx +28 -0
  218. package/src/common/table/helper.ts +33 -0
  219. package/src/common/table/index.ts +2 -0
  220. package/src/common/table/types.d.ts +57 -0
  221. package/src/common/text-strip/NsTextStrip.spec.tsx +30 -0
  222. package/src/common/text-strip/NsTextStrip.stories.tsx +33 -0
  223. package/src/common/text-strip/NsTextStrip.tsx +47 -0
  224. package/src/common/text-strip/index.ts +2 -0
  225. package/src/common/text-strip/types.d.ts +17 -0
  226. package/src/common/typography/NsTypography.stories.tsx +2 -2
  227. package/src/common/typography/NsTypography.tsx +1 -1
  228. package/src/common/typography/Typography.styled.tsx +4 -4
  229. package/src/common/typography/index.ts +1 -1
  230. package/src/common/util.ts +1 -0
  231. package/src/common/utility/Show.tsx +13 -0
  232. package/src/common/utility/index.ts +1 -0
  233. package/src/common/validations/regex.ts +3 -0
  234. package/src/common/videos/NsVideo.stories.tsx +2 -2
  235. package/src/common/videos/NsVideo.tsx +1 -1
  236. package/src/common/videos/index.ts +1 -1
  237. package/src/common/videos/types.d.ts +1 -1
  238. package/src/common/alert/NsAlert.styled.ts +0 -124
  239. package/src/common/card/tests/NsFfgCard.spec.tsx +0 -23
  240. package/src/common/column-control/NDivs.story.tsx +0 -35
  241. package/src/common/common.types.ts +0 -10
  242. /package/src/common/brand-components/{NsBrandContanier.styled.tsx → NsBrandContainer.styled.tsx} +0 -0
  243. /package/src/common/card/{tests → specs}/BrandLogo.spec.tsx +0 -0
  244. /package/src/common/card/{tests → specs}/NsBgCard.spec.tsx +0 -0
  245. /package/src/common/card/{tests → specs}/NsBrandProductCard.spec.tsx +0 -0
  246. /package/src/common/carousal/{tests → specs}/Chevron.spec.tsx +0 -0
  247. /package/src/common/carousal/{tests → specs}/RenderBrand.spec.tsx +0 -0
@@ -18,22 +18,7 @@ export type NsColumnControlProps = PropsWithChildren<{
18
18
  * ns-col-md-(column-ratio) (for tablet screens >= 600px),
19
19
  * and ns-col-sm-(column-ratio) (for mobile screens i.e < 600px)
20
20
  * sample values
21
- * 'ns-col-lg-2-1',
22
- 'ns-col-lg-1-2',
23
- 'ns-col-lg-1-1',
24
- 'ns-col-lg-1-1-1',
25
- 'ns-col-lg-2-1-1',
26
- 'ns-col-lg-1-1-1-1',
27
- 'ns-col-md-1',
28
- 'ns-col-md-1-1',
29
- 'ns-col-md-1-2',
30
- 'ns-col-md-2-1',
31
- 'ns-col-md-1-1-1',
32
- 'ns-col-md-2-1-1',
33
- 'ns-col-sm-1',
34
- 'ns-col-sm-1-1',
35
- 'ns-col-sm-1-2',
36
- 'ns-col-sm-2-1',
21
+ * 'ns-col-lg-2-1', 'ns-col-lg-1-2', 'ns-col-lg-1-1',
37
22
  */
38
23
  columnClass: string;
39
24
  gap?: number;
@@ -45,3 +30,17 @@ export type sizeObjectType = {
45
30
  tabletFractions?: number[];
46
31
  mobileFractions?: number[];
47
32
  };
33
+
34
+ export type NsFlexColumnsProps = PropsWithChildren<{
35
+ /**
36
+ * follows the convention of ns-col-lg-{column-ratio}
37
+ * sample value for column-ratio = 2-1 ==>ns-col-lg-2-1
38
+ */
39
+ columnClass: string;
40
+ gap: number;
41
+ /* when this boolean is true, column control takes full width of page instead of being restricted to 1200px */
42
+ isFullWidth?: boolean;
43
+ }> &
44
+ AdditionalProps;
45
+
46
+ export type NsGridColumnsProps = NsFlexColumnsProps;
@@ -1,5 +1,5 @@
1
1
  import { ValueOf } from 'next/dist/shared/lib/constants';
2
- import { NsColumnControlProps, sizeObjectType } from './types';
2
+ import { NsColumnControlProps, sizeObjectType } from './types.d';
3
3
 
4
4
  export const columnClasses: NsColumnControlProps['columnClass'][] = [
5
5
  'ns-col-lg-2-1',
@@ -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
  }
@@ -0,0 +1,21 @@
1
+ import { FoundationTheme } from '@nuskin/foundation-theme';
2
+
3
+ export type CslpTag = { 'data-cslp': string };
4
+
5
+ export type AdditionalProps = {
6
+ additionalProps?: {
7
+ ariaLabel?: string;
8
+ testid?: string;
9
+ className?: string;
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;
21
+ };
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { render, fireEvent, screen, waitFor } from '../../../test-utils';
2
+ import { render, fireEvent, screen, waitFor } from '@/test-utils/index';
3
3
  import NsDropdown from './NsDropdown';
4
4
  import NsDropdownOption from './NsDropdownOption';
5
5
 
@@ -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,7 @@
1
1
  import React, { useState } from 'react';
2
2
  import { DropdownContainer, DropdownButton, DropdownList, DropdownHead } from './NsDropdown.styled';
3
3
  import { NsIcon } from '../icon';
4
- import { DropdownProps } from './types';
4
+ import { DropdownProps } from './types.d';
5
5
 
6
6
  const NsDropdown: React.FC<DropdownProps> = ({
7
7
  selectedValue,
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { ListItem, ListItemContainer } from './NsDropdown.styled';
3
- import { DropdownOptionProps } from './types';
3
+ import { DropdownOptionProps } from './types.d';
4
4
 
5
5
  export const NsDropdownOption: React.FC<DropdownOptionProps> = ({
6
6
  value,
@@ -1,3 +1,3 @@
1
1
  export { default as NsDropdown } from './NsDropdown';
2
2
  export { default as NsDropdownOption } from './NsDropdownOption';
3
- export type { DropdownOptionProps, DropdownProps } from './types';
3
+ export type { DropdownOptionProps, DropdownProps } from './types.d';
@@ -1,14 +1,14 @@
1
1
  import React, { useState } from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { NsDropdown, NsDropdownOption } from '../';
4
- import { withCenterDecorator } from '../../../stories/decorators/withCenterDecorator';
4
+ import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
5
5
 
6
6
  type ComponentType = typeof NsDropdown;
7
7
  type Story = StoryObj<ComponentType>;
8
8
  type MetaStory = Meta<ComponentType>;
9
9
 
10
10
  export default {
11
- title: 'Components/NsDropdown',
11
+ title: 'Components/Inputs/NsDropdown',
12
12
  decorators: [withCenterDecorator],
13
13
  component: NsDropdown,
14
14
  argTypes: {
@@ -1,14 +1,14 @@
1
1
  import React, { useState } from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { NsDropdown, NsDropdownOption } from '../';
4
- import { withCenterDecorator } from '../../../stories/decorators/withCenterDecorator';
4
+ import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
5
5
 
6
6
  type ComponentType = typeof NsDropdown;
7
7
  type Story = StoryObj<ComponentType>;
8
8
  type MetaStory = Meta<ComponentType>;
9
9
 
10
10
  export default {
11
- title: 'Components/NsDropdown',
11
+ title: 'Components/Inputs/NsDropdown',
12
12
  decorators: [withCenterDecorator],
13
13
  component: NsDropdown,
14
14
  argTypes: {
@@ -1,7 +1,9 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { FoundationTheme } from '@nuskin/foundation-theme';
3
3
 
4
- export interface DropdownProps {
4
+ import { AdditionalProps } from '../common.types';
5
+
6
+ export interface DropdownProps extends AdditionalProps {
5
7
  selectedValue: string;
6
8
  selectedLabel: string;
7
9
  onSelect: (arg: string) => void; // eslint-disable-line
@@ -0,0 +1,145 @@
1
+ import React from 'react';
2
+ import { render, screen, act } from '@testing-library/react';
3
+ import NsEmailSimple from './NsEmailSimple';
4
+ import { userEvent } from '@storybook/testing-library';
5
+ import { emailPattern } from '../validations/regex';
6
+
7
+ describe('NsEmailSimple', () => {
8
+ let mockCheckEmail = jest.fn().mockResolvedValue([true, false]);
9
+ let propsData = {
10
+ disabled: false,
11
+ label: 'Email',
12
+ required: true,
13
+ maxLength: 50,
14
+ invalidEmailMessage: 'Please enter a valid email',
15
+ dupedEmailMessage: 'Email ID already exists',
16
+ requiredErrorMessage: 'Email field is required',
17
+ checkEmail: mockCheckEmail,
18
+ };
19
+ it('renders email field with invalid regex error', async () => {
20
+ render(<NsEmailSimple {...propsData} />);
21
+ const emailField = (await screen.findByTestId('email-field-test')) as HTMLInputElement;
22
+ expect(emailField).toBeInTheDocument;
23
+ const inputElement = emailField.querySelector('input');
24
+ if (!inputElement) {
25
+ return;
26
+ }
27
+ act(() => {
28
+ userEvent.type(inputElement, 'test');
29
+ userEvent.tab();
30
+ });
31
+
32
+ expect(inputElement).toHaveValue('test');
33
+
34
+ act(() => {
35
+ userEvent.clear(inputElement);
36
+ userEvent.type(inputElement, 'test@gm');
37
+ userEvent.tab();
38
+ });
39
+ expect(inputElement).toHaveValue('test@gm');
40
+
41
+ mockCheckEmail = jest.fn().mockResolvedValue([true, true]);
42
+ render(<NsEmailSimple {...propsData} />);
43
+ act(() => {
44
+ userEvent.clear(inputElement);
45
+ userEvent.type(inputElement, 'test!gmail');
46
+ userEvent.tab();
47
+ });
48
+ expect(inputElement).toHaveValue('test!gmail');
49
+ act(() => {
50
+ userEvent.clear(inputElement);
51
+ userEvent.tab();
52
+ });
53
+ expect(inputElement).toHaveValue('');
54
+
55
+ mockCheckEmail = jest.fn().mockResolvedValue([true, false]);
56
+ render(<NsEmailSimple {...propsData} />);
57
+ act(() => {
58
+ userEvent.clear(inputElement);
59
+ userEvent.type(inputElement, 'test@gmail.co');
60
+ userEvent.tab();
61
+ });
62
+ expect(inputElement).toHaveValue('test@gmail.co');
63
+ });
64
+ it('renders email field with invalid error', async () => {
65
+ let mockCheckEmail1 = jest.fn().mockResolvedValue([false, false]);
66
+ let propsData1 = {
67
+ disabled: false,
68
+ label: 'Email',
69
+ required: true,
70
+ maxLength: 50,
71
+ invalidEmailMessage: 'Please enter a valid email',
72
+ dupedEmailMessage: 'Email ID already exists',
73
+ requiredErrorMessage: 'Email field is required',
74
+ checkEmail: mockCheckEmail1,
75
+ };
76
+ render(<NsEmailSimple {...propsData1} />);
77
+ const emailField = (await screen.findByTestId('email-field-test')) as HTMLInputElement;
78
+ expect(emailField).toBeInTheDocument;
79
+ const inputElement = emailField.querySelector('input');
80
+ if (!inputElement) {
81
+ return;
82
+ }
83
+ act(() => {
84
+ userEvent.clear(inputElement);
85
+ userEvent.type(inputElement, 'test@gmail.com');
86
+ userEvent.tab();
87
+ });
88
+ expect(inputElement).toHaveValue('test@gmail.com');
89
+ expect(inputElement.value).toMatch(emailPattern);
90
+ });
91
+ it('renders email field without error', async () => {
92
+ let mockCheckEmail1 = jest.fn().mockResolvedValue([true, true]);
93
+ let propsData1 = {
94
+ disabled: false,
95
+ label: 'Email',
96
+ required: true,
97
+ maxLength: 50,
98
+ invalidEmailMessage: 'Please enter a valid email',
99
+ dupedEmailMessage: 'Email ID already exists',
100
+ requiredErrorMessage: 'Email field is required',
101
+ checkEmail: mockCheckEmail1,
102
+ };
103
+ render(<NsEmailSimple {...propsData1} />);
104
+ const emailField = (await screen.findByTestId('email-field-test')) as HTMLInputElement;
105
+ expect(emailField).toBeInTheDocument;
106
+ const inputElement = emailField.querySelector('input');
107
+ if (!inputElement) {
108
+ return;
109
+ }
110
+ act(() => {
111
+ userEvent.clear(inputElement);
112
+ userEvent.type(inputElement, 'test@gmail.com');
113
+ userEvent.tab();
114
+ });
115
+ expect(inputElement).toHaveValue('test@gmail.com');
116
+ expect(inputElement.value).toMatch(emailPattern);
117
+ });
118
+ it('renders email field with API error', async () => {
119
+ let mockCheckEmail1 = jest.fn().mockResolvedValue(undefined);
120
+ let propsData1 = {
121
+ disabled: false,
122
+ label: 'Email',
123
+ required: true,
124
+ maxLength: 50,
125
+ invalidEmailMessage: 'Please enter a valid email',
126
+ dupedEmailMessage: 'Email ID already exists',
127
+ requiredErrorMessage: 'Email field is required',
128
+ checkEmail: mockCheckEmail1,
129
+ };
130
+ render(<NsEmailSimple {...propsData1} />);
131
+ const emailField = (await screen.findByTestId('email-field-test')) as HTMLInputElement;
132
+ expect(emailField).toBeInTheDocument;
133
+ const inputElement = emailField.querySelector('input');
134
+ if (!inputElement) {
135
+ return;
136
+ }
137
+ act(() => {
138
+ userEvent.clear(inputElement);
139
+ userEvent.type(inputElement, 'test@gmail.com');
140
+ userEvent.tab();
141
+ });
142
+ expect(inputElement).toHaveValue('test@gmail.com');
143
+ expect(inputElement.value).toMatch(emailPattern);
144
+ });
145
+ });
@@ -0,0 +1,61 @@
1
+ import type { StoryObj, Meta } from '@storybook/react';
2
+ import NsEmailSimple from './NsEmailSimple';
3
+ import { within } from '@storybook/testing-library';
4
+ import { expect } from '@storybook/jest';
5
+ import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
6
+
7
+ async function checkEmail() {
8
+ const isServiceValid = Promise.resolve(true);
9
+ const isEmailAvailable = Promise.resolve(true);
10
+ return [isServiceValid, isEmailAvailable];
11
+ }
12
+ type ComponentType = typeof NsEmailSimple;
13
+ type Story = StoryObj<ComponentType>;
14
+ type MetaStory = Meta<ComponentType>;
15
+
16
+ export default {
17
+ component: NsEmailSimple,
18
+ title: 'Components/Inputs/NsEmailSimple',
19
+ decorators: [withCenterDecorator],
20
+ play: ({ canvasElement }) => {
21
+ const canvas = within(canvasElement);
22
+ const el = canvas.getByTestId('email-field-test');
23
+ expect(el).toBeInTheDocument();
24
+ },
25
+
26
+ argTypes: {
27
+ disabled: {
28
+ description: 'Indicates whether the email field is enabled/disabled',
29
+ },
30
+ label: {
31
+ description: 'Label of the email component',
32
+ },
33
+ required: {
34
+ description: 'Indicates whether the email field is mandatory or not',
35
+ },
36
+ maxLength: {
37
+ description: 'Maximum number of characters allowed',
38
+ },
39
+ invalidEmailMessage: {
40
+ description: 'Displayed when an invalid email is entered',
41
+ },
42
+ dupedEmailMessage: {
43
+ description: 'Displayed when a duplicate email is entered',
44
+ },
45
+ requiredErrorMessage: {
46
+ description: 'Displayed when email is not entered',
47
+ },
48
+ },
49
+ args: {
50
+ disabled: false,
51
+ label: 'Email',
52
+ required: true,
53
+ maxLength: 50,
54
+ invalidEmailMessage: 'Please enter a valid email',
55
+ dupedEmailMessage: 'Email ID already exists',
56
+ requiredErrorMessage: 'Email field is required',
57
+ checkEmail: checkEmail,
58
+ },
59
+ } as MetaStory;
60
+
61
+ export const NsEmail: Story = {};
@@ -0,0 +1,71 @@
1
+ import React, { useState } from 'react';
2
+ import { NsEmailSimpleProps } from './types';
3
+ import { NsInputField } from '../input';
4
+ import { fieldStateType } from '../input/types';
5
+ import { emailPattern } from '../validations/regex';
6
+
7
+ const NsEmailSimple = (props: NsEmailSimpleProps) => {
8
+ const {
9
+ disabled,
10
+ label,
11
+ required,
12
+ maxLength,
13
+ invalidEmailMessage,
14
+ dupedEmailMessage,
15
+ requiredErrorMessage,
16
+ checkEmail,
17
+ additionalProps,
18
+ } = props;
19
+ const [fieldState, setFieldState] = useState<fieldStateType>();
20
+ const [errorMsg, setErrorMessage] = useState('');
21
+
22
+ const handleBlur = async (e: React.FocusEvent<HTMLInputElement>) => {
23
+ const emailInput = e.target.value.toLowerCase().trim();
24
+ let fieldStateValue: fieldStateType;
25
+ let errorMessageValue = '';
26
+ if (required && !emailInput) {
27
+ fieldStateValue = 'error';
28
+ errorMessageValue = requiredErrorMessage;
29
+ } else if (!emailPattern.test(emailInput)) {
30
+ fieldStateValue = 'error';
31
+ errorMessageValue = invalidEmailMessage;
32
+ } else if (typeof checkEmail === 'function') {
33
+ try {
34
+ const [isValid, isAvailable] = await checkEmail();
35
+ if (!isValid) {
36
+ fieldStateValue = 'error';
37
+ errorMessageValue = invalidEmailMessage;
38
+ } else if (!isAvailable) {
39
+ fieldStateValue = 'error';
40
+ errorMessageValue = dupedEmailMessage;
41
+ } else {
42
+ fieldStateValue = undefined;
43
+ errorMessageValue = '';
44
+ }
45
+ } catch (error) {
46
+ console.log('API error', error);
47
+ }
48
+ } else {
49
+ //TODO What happens when user does not pass valid checkEmail function
50
+ }
51
+ setFieldState(fieldStateValue);
52
+ setErrorMessage(errorMessageValue);
53
+ };
54
+ return (
55
+ <NsInputField
56
+ required={required}
57
+ disabled={disabled}
58
+ label={label}
59
+ onBlur={handleBlur}
60
+ inputProps={{ maxLength }}
61
+ additionalProps={{ testid: additionalProps?.testid ?? 'email-field-test' }}
62
+ helperText={errorMsg}
63
+ fieldState={fieldState}
64
+ InputProps={{
65
+ endAdornment: null,
66
+ }}
67
+ />
68
+ );
69
+ };
70
+
71
+ export default NsEmailSimple;
@@ -0,0 +1,2 @@
1
+ export { default as NsEmailSimple } from './NsEmailSimple';
2
+ export type { NsEmailSimpleProps } from './types.d';
@@ -0,0 +1,14 @@
1
+ import { TextFieldProps } from '@mui/material';
2
+ import { AdditionalProps } from '../common.types';
3
+
4
+ export type NsEmailSimpleProps = Pick<TextFieldProps> &
5
+ AdditionalProps & {
6
+ disabled?: boolean;
7
+ label?: string;
8
+ required?: boolean;
9
+ maxLength?: number;
10
+ invalidEmailText?: string;
11
+ dupedEmailText?: string;
12
+ requiredErrorMessage?: string;
13
+ checkEmail?: Function;
14
+ };
@@ -1,35 +1,15 @@
1
- /**
2
- * @author [vijayaprathap Kumar] (vpkumar@nuskin.com)
3
- * Flag.spec.tsx
4
- * @description In this test case,
5
- * we use the render function to render the MyComponent with the desired background color and text.
6
- */
7
-
8
1
  import React from 'react';
9
- import nuskinTheme from '@nuskin/foundation-theme/src/themes/genome-foundation';
10
-
11
- import { render } from '../../../test-utils/index';
12
- import NsFlag from './NsFlag';
13
2
 
14
- const { palette } = nuskinTheme;
3
+ import { render } from '@/test-utils/index';
4
+ import { NsFlag } from '../';
15
5
 
16
- // Test suite for FlagComponent
17
6
  describe('FlagComponent', () => {
18
- // Test case for rendering a div element with background color and text.
19
7
  it('renders a div element with background color and text', () => {
20
- // Render FlagComponent with blue background color and "Nuskin" text
8
+ const text = 'NU SKIN';
21
9
  const { getByText } = render(<NsFlag message="NU SKIN" color="accentColorA" />);
10
+ const divElement = getByText(text);
22
11
 
23
- // Get the rendered div element using its text
24
- const divElement = getByText('NU SKIN');
25
-
26
- // Assert that the div element is in the document
27
12
  expect(divElement).toBeInTheDocument();
28
-
29
- // Assert that the div element has the expected background color
30
- expect(divElement).toHaveStyle(`color: ${palette?.primary?.contrastText}`);
31
-
32
- // Assert that the div element has the expected text content
33
- expect(divElement).toHaveTextContent('NU SKIN');
13
+ expect(divElement).toHaveTextContent(text);
34
14
  });
35
15
  });
@@ -1,5 +1,5 @@
1
1
  import type { StoryObj, Meta } from '@storybook/react';
2
- import { withCenterDecorator } from '../../stories/decorators/withCenterDecorator';
2
+ import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
3
3
  import { NsFlag } from './';
4
4
 
5
5
  type ComponentType = typeof NsFlag;
@@ -7,7 +7,7 @@ type Story = StoryObj<ComponentType>;
7
7
  type MetaStory = Meta<ComponentType>;
8
8
 
9
9
  export default {
10
- title: 'Components/NsFlag',
10
+ title: 'Components/Data Display/NsFlag',
11
11
  component: NsFlag,
12
12
  decorators: [withCenterDecorator],
13
13
  argTypes: {
@@ -7,7 +7,9 @@
7
7
  import { Theme } from '@mui/material';
8
8
  import { styled, ColorUtils, FoundationTheme } from '@nuskin/foundation-theme';
9
9
 
10
- import { NsFlagSize } from './types';
10
+ import { NsFlagSize } from './types.d';
11
+
12
+ const { paletteColor } = ColorUtils;
11
13
 
12
14
  type NsFlagThemeParams = {
13
15
  $size: NsFlagSize;
@@ -33,13 +35,9 @@ const makeFlagSize = ({ $size }: { $size: NsFlagSize }) => {
33
35
  return sizeValue / FLAG_SCALE_VALUE;
34
36
  };
35
37
 
36
- const {
37
- getPaletteColor,
38
- } = ColorUtils;
39
-
40
- const makeFlagBorder = ({ $size, $bgColor = 'accentColorA', theme}: NsFlagThemeParams) => {
38
+ const makeFlagBorder = ({ $size, $bgColor = 'accentColorA', theme }: NsFlagThemeParams) => {
41
39
  if (theme) {
42
- return `${makeFlagSize({ $size })}px solid ${getPaletteColor(theme, $bgColor)}`;
40
+ return `${makeFlagSize({ $size })}px solid ${paletteColor($bgColor)({ theme })}`;
43
41
  }
44
42
  return '';
45
43
  };
@@ -47,8 +45,8 @@ const makeFlagBorder = ({ $size, $bgColor = 'accentColorA', theme}: NsFlagThemeP
47
45
  export const StyledFlag = styled('span')<NsFlagThemeParams>`
48
46
  min-width: 29px;
49
47
  height: ${getFlagHeight}px;
50
- color: ${({ $bgColor = 'accentColorA', theme }) => getPaletteColor(theme, $bgColor, 'contrastText')};
51
- background: ${({ $bgColor = 'accentColorA', theme }) => getPaletteColor(theme, $bgColor)};
48
+ color: ${({ $bgColor = 'accentColorA', theme }) => paletteColor($bgColor, 'contrastText')({ theme })};
49
+ background: ${({ $bgColor = 'accentColorA', theme }) => paletteColor($bgColor)({ theme })};
52
50
  position: relative;
53
51
  padding: 0 15px;
54
52
  display: inline-grid;
@@ -5,10 +5,8 @@
5
5
  */
6
6
 
7
7
  import React, { PropsWithChildren } from 'react';
8
-
9
8
  import { StyledFlag } from './NsFlag.styled';
10
-
11
- import { NsFlagProps } from './types';
9
+ import { NsFlagProps } from './types.d';
12
10
 
13
11
  const NsFlag = (props: PropsWithChildren<NsFlagProps>) => {
14
12
  const { className = '', size = 'small', color = 'accentColorA', message, testId = 'ns-flag-component' } = props;
@@ -1,2 +1,2 @@
1
1
  export { default as NsFlag } from './NsFlag';
2
- export type { NsFlagProps, NsFlagSize } from './types';
2
+ export type { NsFlagProps, NsFlagSize } from './types.d';