@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
@@ -1,96 +1,141 @@
1
1
  import React from 'react';
2
- import { render, screen, waitFor } from '@testing-library/react';
3
- import { NsAutocomplete } from './';
2
+ import { render, screen, waitFor } from '@/test-utils/index';
3
+ import { NsAutocomplete } from '@/common/index';
4
4
  import { OptionUiComponent } from './mock/MockUI';
5
5
  import { AutocompleteMockList } from './mock/MockData';
6
6
  import userEvent from '@testing-library/user-event';
7
+ import { act } from 'react-dom/test-utils';
7
8
 
8
9
  describe('CustomAutocomplete', () => {
9
- test('should render autocomplete component', () => {
10
+ it('1. should render autocomplete component', () => {
10
11
  render(
11
12
  <NsAutocomplete
12
13
  options={AutocompleteMockList}
13
- onSelect={() => {}}
14
14
  placeholder="autocomplete-input"
15
- renderOptions={OptionUiComponent}
15
+ OptionsUI={OptionUiComponent}
16
+ inputValue=""
17
+ value=""
18
+ />
19
+ );
20
+ const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
21
+ expect(autocompleteInput).toBeInTheDocument();
22
+ });
23
+ it('2. check that onChange gets called', () => {
24
+ const onSelectMock = jest.fn();
25
+ const onChangeMock = jest.fn();
26
+ render(
27
+ <NsAutocomplete
28
+ options={AutocompleteMockList}
29
+ onSelect={onSelectMock}
30
+ onChange={onChangeMock}
31
+ placeholder="autocomplete-input"
32
+ OptionsUI={OptionUiComponent}
33
+ inputValue=""
34
+ value=""
16
35
  />
17
36
  );
18
37
  const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
19
38
  expect(autocompleteInput).toBeInTheDocument();
39
+ act(() => {
40
+ userEvent.type(autocompleteInput, 'GS Facial');
41
+ });
42
+ expect(onChangeMock).toHaveBeenCalled();
20
43
  });
21
44
 
22
- test('displays options when typing in the input', async () => {
45
+ it('3. displays options when typing in the input', async () => {
23
46
  render(
24
47
  <NsAutocomplete
25
48
  options={AutocompleteMockList}
26
49
  onSelect={() => {}}
27
- renderOptions={OptionUiComponent}
50
+ OptionsUI={OptionUiComponent}
28
51
  placeholder="autocomplete-input"
52
+ inputValue=""
53
+ value=""
29
54
  />
30
55
  );
31
56
  const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
32
- userEvent.type(autocompleteInput, 'GS Facial');
33
- await waitFor(() => {
34
- const option1 = screen.getByText('GS Facial Gels 2pk Subscription');
35
- expect(option1).toBeInTheDocument();
57
+ act(() => {
58
+ userEvent.type(autocompleteInput, 'GS Facial');
36
59
  });
37
- });
60
+ await waitFor(
61
+ () => {
62
+ const option1 = screen.getByText('GS Facial Gels 2pk Subscription');
63
+ expect(option1).toBeInTheDocument();
64
+ },
65
+ { timeout: 5000 }
66
+ );
67
+ }, 40000);
38
68
 
39
- test('selects an option when clicking on it', async () => {
69
+ it('4. selects an option when clicking on it', async () => {
70
+ const onSelectMock = jest.fn();
40
71
  render(
41
72
  <NsAutocomplete
73
+ onSelect={onSelectMock}
42
74
  options={AutocompleteMockList}
43
- renderOptions={OptionUiComponent}
75
+ OptionsUI={OptionUiComponent}
44
76
  placeholder="autocomplete-input"
77
+ inputValue=""
78
+ value=""
45
79
  />
46
80
  );
47
81
  const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
48
- userEvent.type(autocompleteInput, 'GS Facial');
82
+ act(() => {
83
+ userEvent.type(autocompleteInput, 'GS Facial');
84
+ });
49
85
  await waitFor(() => {
50
86
  const optionLi = screen.getByText('GS Facial Spa');
51
87
  userEvent.click(optionLi);
52
88
  });
53
- });
89
+ expect(onSelectMock).toHaveBeenCalled();
90
+ }, 40000);
54
91
 
55
- test('should load more options when clicking load more button', async () => {
92
+ it('5. should load more options when clicking load more button', async () => {
56
93
  render(
57
94
  <NsAutocomplete
58
95
  options={AutocompleteMockList}
59
- renderOptions={OptionUiComponent}
96
+ OptionsUI={OptionUiComponent}
60
97
  placeholder="autocomplete-input"
61
98
  seeMore={{
62
99
  text: '',
63
100
  show: true,
64
101
  onSeeMore: () => ({}),
65
102
  }}
103
+ inputValue=""
104
+ value=""
66
105
  />
67
106
  );
68
107
  const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
69
- userEvent.type(autocompleteInput, 'GS Facial');
108
+ act(() => {
109
+ userEvent.type(autocompleteInput, 'GS Facial');
110
+ });
70
111
  await waitFor(() => {
71
112
  const loadMoreButton = screen.getByTestId('autocomplete-loadmore-id');
72
113
  userEvent.click(loadMoreButton);
73
114
  });
74
- });
115
+ }, 40000);
75
116
 
76
- test('should clear options when clicking clear button', async () => {
117
+ it('6. should clear options when clicking clear button', async () => {
77
118
  render(
78
119
  <NsAutocomplete
79
120
  options={AutocompleteMockList}
80
- renderOptions={OptionUiComponent}
121
+ OptionsUI={OptionUiComponent}
81
122
  placeholder="autocomplete-input"
82
123
  clear={{
83
124
  text: '',
84
125
  show: true,
85
126
  onClear: () => ({}),
86
127
  }}
128
+ inputValue=""
129
+ value=""
87
130
  />
88
131
  );
89
132
  const autocompleteInput = screen.getByPlaceholderText('autocomplete-input');
90
- userEvent.type(autocompleteInput, 'GS Facial');
133
+ act(() => {
134
+ userEvent.type(autocompleteInput, 'GS Facial');
135
+ });
91
136
  await waitFor(() => {
92
137
  const clearButton = screen.getByTestId('autocomplete-clear-id');
93
138
  userEvent.click(clearButton);
94
139
  });
95
- });
140
+ }, 40000);
96
141
  });
@@ -4,21 +4,25 @@
4
4
  * @description NsAutocomplete component.
5
5
  */
6
6
 
7
- import React from 'react';
7
+ import React, { useState } from 'react';
8
8
  import type { StoryObj, Meta } from '@storybook/react';
9
9
 
10
- import { withCenterDecorator } from '../../stories/decorators/withCenterDecorator';
10
+ import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
11
11
  import { NsAutocomplete } from './';
12
12
  import { AutocompleteMockList } from './mock/MockData';
13
13
  import { OptionUiComponent } from './mock/MockUI';
14
- import { NsAutocompleteProps } from './types';
14
+ import { NsAutocompleteProps, OptionsProps } from './types.d';
15
+ import { NsIcon } from '../icon';
16
+ import { IconButton } from '@mui/material';
15
17
 
16
18
  type ComponentType = typeof NsAutocomplete;
17
19
  type Story = StoryObj<ComponentType>;
18
20
  type MetaStory = Meta<ComponentType>;
19
21
 
22
+ const PAGE_SIZE = 6;
23
+
20
24
  export default {
21
- title: 'Components/NsAutocomplete',
25
+ title: 'Components/Inputs/NsAutocomplete',
22
26
  component: NsAutocomplete,
23
27
  decorators: [withCenterDecorator],
24
28
  argTypes: {
@@ -30,22 +34,75 @@ export default {
30
34
  options: AutocompleteMockList,
31
35
  seeMore: {
32
36
  text: 'See More Results',
33
- show: false,
37
+ show: true,
34
38
  onSeeMore: () => {
35
39
  throw new Error('Function not implemented.');
36
40
  },
37
41
  },
38
42
  clear: {
39
43
  text: 'Clear Recent Searches',
40
- show: false,
44
+ show: true,
41
45
  onClear: () => {
42
46
  throw new Error('Function not implemented.');
43
47
  },
44
48
  },
49
+ iconStart: <NsIcon name="search" />,
50
+ iconEnd: <NsIcon name="close" size="small" />,
45
51
  placeholder: 'search',
46
52
  },
47
53
  } as MetaStory;
48
54
 
55
+ const RenderAutocomplete = (args: NsAutocompleteProps) => {
56
+ const [selectedOption, setSelectedOption] = useState<string | OptionsProps | null>(null);
57
+ const [searchTerms, setSearchTerms] = useState<string>('');
58
+ const [pageNo, setPageNo] = useState(PAGE_SIZE);
59
+
60
+ const onSeeMoreHandler = () => {
61
+ setPageNo(PAGE_SIZE + PAGE_SIZE);
62
+ };
63
+
64
+ const AutoCompleteProps = {
65
+ ...args,
66
+ seeMore: {
67
+ ...args.seeMore,
68
+ onSeeMore: onSeeMoreHandler,
69
+ },
70
+ } as NsAutocompleteProps;
71
+
72
+ const onSelect = (value: string | OptionsProps | null) => {
73
+ setSelectedOption(value);
74
+ setSearchTerms((value = ''));
75
+ };
76
+
77
+ const handleInputChange = (newValue: string) => {
78
+ setSearchTerms(newValue);
79
+ };
80
+
81
+ const onInputClear = () => {
82
+ setSelectedOption(null);
83
+ setSearchTerms('');
84
+ };
85
+
86
+ return (
87
+ <div>
88
+ <NsAutocomplete
89
+ {...AutoCompleteProps}
90
+ options={args.options.slice(0, pageNo)}
91
+ onChange={handleInputChange}
92
+ onSelect={onSelect}
93
+ OptionsUI={OptionUiComponent}
94
+ iconEnd={
95
+ <IconButton onClick={onInputClear}>
96
+ <NsIcon name="close" size="small" />
97
+ </IconButton>
98
+ }
99
+ value={selectedOption}
100
+ inputValue={searchTerms}
101
+ />
102
+ </div>
103
+ );
104
+ };
105
+
49
106
  export const Autocomplete: Story = {
50
- render: (args: NsAutocompleteProps) => <NsAutocomplete {...args} renderOptions={OptionUiComponent} />,
107
+ render: RenderAutocomplete,
51
108
  };
@@ -0,0 +1,12 @@
1
+ import { styled } from '@nuskin/foundation-theme';
2
+
3
+ import { NsInputField } from '../input';
4
+
5
+ export const StyledTextField = styled(NsInputField)`
6
+ .MuiAutocomplete-inputRoot {
7
+ padding-right: 10px !important;
8
+ }
9
+ .muiautocomplete-popupindicator {
10
+ display: none;
11
+ }
12
+ `;
@@ -1,92 +1,140 @@
1
- import * as React from 'react';
2
- import { NsInputField } from '../input';
1
+ import React, { HTMLAttributes, PropsWithChildren, useCallback, useEffect, useRef, useState } from 'react';
3
2
  import { Autocomplete, AutocompleteRenderGroupParams, List, AutocompleteRenderInputParams } from '@mui/material';
4
- import { useState, PropsWithChildren, useRef } from 'react';
5
- import { NsTypography } from '../typography';
6
- import { NsAutocompleteProps, OptionsProps, ComponentboxProps } from './types';
7
- import { NsButton } from '../button';
3
+ import { NsTypography, NsButton } from '../';
4
+ import { NsAutocompleteProps, OptionsProps, ComponentBoxProps } from './types.d';
5
+ import { StyledTextField } from './NsAutocomplete.styled';
6
+
7
+ const renderOptionLabel = (option: OptionsProps) => option.value;
8
+
9
+ const renderGroupBy = (option: OptionsProps) => option.group as string;
8
10
 
9
11
  const NsAutocomplete = ({
10
12
  id = 'custom_autocomplete_dropdown',
11
- options = [],
12
- renderOptions,
13
+ options,
14
+ OptionsUI,
15
+ loading,
16
+ noOptionsText,
13
17
  onSelect,
18
+ onChange,
14
19
  clear,
15
20
  seeMore,
16
21
  placeholder,
17
22
  className,
23
+ iconStart,
24
+ iconEnd,
25
+ inputValue,
26
+ value,
18
27
  }: PropsWithChildren<NsAutocompleteProps>) => {
19
- const inputRef = useRef(null);
28
+ const [scrollTopPosition, setScrollTopPosition] = useState(0);
29
+ const listboxRef = useRef<HTMLUListElement | null>(null);
20
30
 
21
- const [selectedOption, setSelectedOption] = useState<OptionsProps | null>(null);
22
- const OptionUI = renderOptions;
31
+ useEffect(() => {
32
+ if (listboxRef?.current && options.length > 0) {
33
+ listboxRef.current.scrollTop = scrollTopPosition;
34
+ }
35
+ }, [options, scrollTopPosition]);
23
36
 
24
- const handleSelect = (event: React.SyntheticEvent<Element, Event>, value: OptionsProps | null) => {
25
- setSelectedOption(value);
26
- onSelect ? onSelect(value) : null;
27
- };
37
+ const handleSelect = useCallback(
38
+ (event: React.SyntheticEvent, value: string | OptionsProps | null) => {
39
+ if (typeof onSelect === 'function') {
40
+ onSelect(value);
41
+ }
42
+ },
43
+ [onSelect]
44
+ );
45
+
46
+ const handleInputChange = useCallback(
47
+ (event: React.SyntheticEvent, newInputValue: string) => {
48
+ if (typeof onChange === 'function') {
49
+ onChange(newInputValue);
50
+ }
51
+ },
52
+ [onChange]
53
+ );
28
54
 
29
55
  const renderInput = (params: AutocompleteRenderInputParams) => (
30
- <NsInputField {...params} placeholder={placeholder} inputRef={inputRef} />
56
+ <StyledTextField
57
+ {...params}
58
+ placeholder={placeholder}
59
+ InputProps={{
60
+ ...params.InputProps,
61
+ startAdornment: iconStart,
62
+ endAdornment: iconEnd,
63
+ }}
64
+ />
31
65
  );
32
66
 
33
- const renderOption = (props: ComponentboxProps, option: OptionsProps) => (
34
- <li {...props}>
35
- <OptionUI {...props} option={option} />
67
+ // TODO: @vijayprathap need to check if we can remove object spread of ...props and ...params in renderInput
68
+ const renderOption = (props: ComponentBoxProps, option: OptionsProps) => (
69
+ <li {...props} key={props.id}>
70
+ {OptionsUI ? <OptionsUI {...props} option={option} /> : null}
36
71
  </li>
37
72
  );
38
73
 
39
- const renderOptionLabel = (option: OptionsProps) => option.value;
40
-
41
- const renderGroupBy = (option: OptionsProps) => option.group as string;
42
-
43
74
  const renderGroupName = ({ key, group, children }: AutocompleteRenderGroupParams) => (
44
75
  <li key={key}>
45
76
  {group ? (
46
- <NsTypography variant="p-s" className="p-3">
77
+ <NsTypography variant="p-m" weight="bold" className="p-3">
47
78
  {group}
48
79
  </NsTypography>
49
80
  ) : null}
50
- <span>{children}</span>
81
+ <ul className="p-0">{children}</ul>
51
82
  </li>
52
83
  );
53
84
 
54
- const renderClearButton = () => {
55
- const { show, onClear, text = 'See More Results' } = clear || {};
85
+ const renderClearButton = useCallback(() => {
86
+ const { show, onClear, text = 'Clear Search' } = clear || {};
56
87
  if (!show) {
57
88
  return null;
58
89
  }
59
90
  return (
60
- <div className="text-right p-3">
91
+ <li className="text-right p-3">
61
92
  <NsButton onClick={onClear} data-testid="autocomplete-clear-id">
62
- <NsTypography variant={'p-xs'}>{text}</NsTypography>
93
+ <NsTypography variant="p-xs" weight="bold">
94
+ {text}
95
+ </NsTypography>
63
96
  </NsButton>
64
- </div>
97
+ </li>
65
98
  );
66
- };
99
+ }, [clear]);
100
+
101
+ const handleSeeMore = useCallback(() => {
102
+ const { onSeeMore } = seeMore || {};
103
+ // Remember the current scroll position
104
+ if (options?.length > 0 && listboxRef?.current) {
105
+ const lastScrollPosition = listboxRef.current?.scrollTop;
106
+ if (lastScrollPosition) {
107
+ setScrollTopPosition(lastScrollPosition);
108
+ }
109
+ }
110
+ if (onSeeMore && typeof onSeeMore === 'function') {
111
+ onSeeMore();
112
+ }
113
+ }, [seeMore, options]);
67
114
 
68
- const renderSeeMoreButton = () => {
69
- const { show, onSeeMore, text = 'See More Results' } = seeMore || {};
115
+ const renderSeeMoreButton = useCallback(() => {
116
+ const { show, text = 'See More Results' } = seeMore || {};
70
117
  if (!show) {
71
118
  return null;
72
119
  }
120
+
73
121
  return (
74
- <div className="text-center p-3">
75
- <NsButton onClick={onSeeMore} data-testid="autocomplete-loadmore-id">
76
- <NsTypography variant="p-xs">{text}</NsTypography>
122
+ <li className="text-center p-3">
123
+ <NsButton onClick={handleSeeMore} data-testid="autocomplete-loadmore-id">
124
+ <NsTypography variant="p-xs" weight="bold">
125
+ {text}
126
+ </NsTypography>
77
127
  </NsButton>
78
- </div>
128
+ </li>
79
129
  );
80
- };
130
+ }, [seeMore, handleSeeMore]);
81
131
 
82
- const renderListBoxComponent = ({ children, ...otherProps }: ComponentboxProps) => (
83
- <>
132
+ const renderListBoxComponent = ({ children, ...otherProps }: ComponentBoxProps) => (
133
+ <List {...otherProps} ref={listboxRef}>
84
134
  {renderClearButton()}
85
- <List {...otherProps}>
86
- {children}
87
- {renderSeeMoreButton()}
88
- </List>
89
- </>
135
+ {children}
136
+ {renderSeeMoreButton()}
137
+ </List>
90
138
  );
91
139
 
92
140
  return (
@@ -95,14 +143,20 @@ const NsAutocomplete = ({
95
143
  disablePortal
96
144
  className={className}
97
145
  options={options}
146
+ loading={loading}
147
+ noOptionsText={noOptionsText}
98
148
  groupBy={renderGroupBy}
99
149
  getOptionLabel={renderOptionLabel}
100
150
  renderOption={renderOption}
101
151
  renderInput={renderInput}
102
152
  ListboxComponent={renderListBoxComponent}
153
+ ListboxProps={{ scrollTopPosition } as HTMLAttributes<HTMLUListElement>} // Pass the current scroll position
103
154
  renderGroup={renderGroupName}
104
155
  onChange={handleSelect}
105
- value={selectedOption}
156
+ onInputChange={handleInputChange}
157
+ value={value}
158
+ inputValue={inputValue}
159
+ freeSolo
106
160
  />
107
161
  );
108
162
  };
@@ -1 +1,2 @@
1
- export { default as NsAutocomplete } from './NsAutocomplete';
1
+ export { default as NsAutocomplete } from './NsAutocomplete';
2
+ export * from './types.d';
@@ -4,24 +4,25 @@ export const AutocompleteMockList: OptionsProps[] = [
4
4
  {
5
5
  id: '1',
6
6
  value: 'GS Facial Gels 2pk Subscription',
7
- // eslint-disable-next-line max-len
7
+ group: 'Popular Searches',
8
8
  image: 'https://www.nuskin.com/content/products/86/99/01/86990168/_jcr_content/fullImage.img.160.160.png.64.64.png',
9
9
  },
10
10
  {
11
11
  id: '2',
12
12
  value: 'GS Facial',
13
- // eslint-disable-next-line max-len
13
+ group: 'Popular Searches',
14
14
  image: 'https://www.nuskin.com/content/products/01/55/00/01550014/_jcr_content/fullImage.img.160.160.png.64.64.png',
15
15
  },
16
16
  {
17
17
  id: '3',
18
- value: 'GS Facial Spa', // eslint-disable-next-line max-len
18
+ value: 'GS Facial Spa',
19
+ group: 'Popular Searches',
19
20
  image: 'https://www.nuskin.com/content/products/01/10/12/01101251/_jcr_content/fullImage.img.160.160.png.64.64.png',
20
21
  },
21
22
  {
22
23
  id: '4',
23
24
  value: 'Nu Skin® Facial Spa',
24
- group: 'Popular Searches', // eslint-disable-next-line max-len
25
+ group: 'Popular Searches',
25
26
  image: 'https://www.nuskin.com/content/products/01/55/00/01550013/_jcr_content/fullImage.img.160.160.png.64.64.png',
26
27
  icon: 'cart',
27
28
  groupBgColor: '#000',
@@ -29,7 +30,7 @@ export const AutocompleteMockList: OptionsProps[] = [
29
30
  {
30
31
  id: '5',
31
32
  value: 'AP 24® Whitening Fluoride Toothpaste',
32
- group: 'Popular Searches', // eslint-disable-next-line max-len
33
+ group: 'Popular Searches',
33
34
  image: 'https://www.nuskin.com/content/products/86/99/01/86990100/_jcr_content/fullImage.img.160.160.png.64.64.png',
34
35
  icon: 'cart',
35
36
  groupBgColor: '#000',
@@ -37,10 +38,52 @@ export const AutocompleteMockList: OptionsProps[] = [
37
38
  {
38
39
  id: '6',
39
40
  value: 'PowerMask + LumiSpa Normal Subscription',
40
- group: 'Popular Searches', // eslint-disable-next-line max-len
41
+ group: 'Popular Searches',
41
42
  image: 'https://www.nuskin.com/content/products/01/00/16/01001647/jcr:content/fullImage.img.160.160.png/1611763135883-cache.png.64.64.png',
42
43
  icon: 'cart',
43
44
  groupBgColor: '#000',
44
45
  },
45
- ];
46
46
 
47
+ {
48
+ id: '7',
49
+ value: 'Rejuvenating Cream',
50
+ group: 'Popular Searches',
51
+ image: 'https://www.nuskin.com/content/products/86/99/01/86990168/_jcr_content/fullImage.img.160.160.png.64.64.png',
52
+ },
53
+ {
54
+ id: '8',
55
+ value: 'Epoch® Firewalker® Foot Cream',
56
+ group: 'Popular Searches',
57
+ image: 'https://www.nuskin.com/content/products/01/55/00/01550014/_jcr_content/fullImage.img.160.160.png.64.64.png',
58
+ },
59
+ {
60
+ id: '9',
61
+ value: 'Tri-Phasic White® Night Cream',
62
+ group: 'Popular Searches',
63
+ image: 'https://www.nuskin.com/content/products/01/10/12/01101251/_jcr_content/fullImage.img.160.160.png.64.64.png',
64
+ },
65
+ {
66
+ id: '10',
67
+ value: 'ThirstFix Hydrating Gel Cream (Sample Size)',
68
+ group: 'Popular Searches',
69
+ image: 'https://www.nuskin.com/content/products/01/55/00/01550013/_jcr_content/fullImage.img.160.160.png.64.64.png',
70
+ icon: 'cart',
71
+ groupBgColor: '#000',
72
+ },
73
+ {
74
+ id: '11',
75
+ value: 'Epoch® Baby Hair',
76
+ group: 'Popular Searches',
77
+ image: 'https://www.nuskin.com/content/products/86/99/01/86990100/_jcr_content/fullImage.img.160.160.png.64.64.png',
78
+ icon: 'cart',
79
+ groupBgColor: '#000',
80
+ },
81
+ {
82
+ id: '12',
83
+ value: '180° Face Wash',
84
+ group: 'Popular Searches',
85
+ image: 'https://www.nuskin.com/content/products/01/00/16/01001647/jcr:content/fullImage.img.160.160.png/1611763135883-cache.png.64.64.png',
86
+ icon: 'cart',
87
+ groupBgColor: '#000',
88
+ },
89
+ ];
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @next/next/no-img-element */
1
2
  import { ListStyled } from './Mock.styled';
2
3
  import { NsTypography } from '../../typography';
3
4
  import { NsIcon } from '../../icon';
@@ -5,7 +6,7 @@ import { ListUiProps } from '../types';
5
6
 
6
7
  const OptionUiComponent = ({ option }: ListUiProps) => {
7
8
  return (
8
- <ListStyled className="checker">
9
+ <ListStyled>
9
10
  <div className="item-description" role="link" tabIndex={0}>
10
11
  <NsTypography aria-label="Nu Skin Tumbler" variant="p-m" colorOverride="#63666A">
11
12
  <img src={option.image} alt="No-image" />
@@ -1,26 +1,36 @@
1
1
  import React, { HTMLAttributes } from 'react';
2
+
3
+ import { ComponentsProps } from '@mui/material';
4
+
2
5
  import { AdditionalProps } from '../common.types';
3
6
  import { NsIconNameType } from '../icon';
4
- import { ComponentsProps } from '@mui/material';
5
7
 
6
8
  export type NsAutocompleteProps = {
7
9
  id?: string;
8
10
  options: OptionsProps[];
9
- renderOptions: React.ComponentType<any>;
11
+ loading?: boolean;
12
+ OptionsUI: React.ComponentType<any>;
13
+ noOptionsText?: string;
14
+ // eslint-disable-next-line no-unused-vars
15
+ onSelect?: (params: string | OptionsProps | null) => void;
10
16
  // eslint-disable-next-line no-unused-vars
11
- onSelect?: (params: OptionsProps | null) => void;
17
+ onChange?: (params: string) => void;
12
18
  clear?: {
13
- text: string;
19
+ text?: string;
14
20
  show?: boolean; //default to false
15
- onClear: () => {};
21
+ onClear: () => void;
16
22
  };
17
23
  seeMore?: {
18
- text: string;
24
+ text?: string;
19
25
  show?: boolean; //default to false
20
- onSeeMore: () => {};
26
+ onSeeMore: () => void;
21
27
  };
22
28
  placeholder?: string;
23
29
  className?: string;
30
+ iconStart?: string | React.ReactNode;
31
+ iconEnd?: string | React.ReactNode;
32
+ inputValue: string;
33
+ value: string | OptionsProps | null;
24
34
  } & AdditionalProps;
25
35
 
26
36
  export type ListUiProps = {
@@ -36,4 +46,4 @@ export type OptionsProps = {
36
46
  groupBgColor?: string;
37
47
  };
38
48
 
39
- export type ComponentboxProps = ComponentsProps & HTMLAttributes<HTMLElement>;
49
+ export type ComponentBoxProps = ComponentsProps & HTMLAttributes<HTMLElement>;