@nuskin/foundation-ui-components 1.0.0-develop.3 → 1.0.0-develop.5

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 (95) hide show
  1. package/package.json +9 -4
  2. package/src/common/accordion/NsAccordion.tsx +77 -0
  3. package/src/common/accordion/NsAccordionHeader.tsx +56 -0
  4. package/src/common/accordion/NsAccordionItem.tsx +39 -0
  5. package/src/common/accordion/NsCollapse.tsx +56 -0
  6. package/src/common/accordion/NsToggleSection.tsx +30 -0
  7. package/src/common/accordion/ToggleSection.styled.tsx +28 -0
  8. package/src/common/accordion/index.ts +6 -0
  9. package/src/common/accordion/specs/NsAccordion.spec.tsx +93 -0
  10. package/src/common/accordion/specs/NsAccordionHeader.spec.tsx +63 -0
  11. package/src/common/accordion/specs/NsAccordionItem.spec.tsx +62 -0
  12. package/src/common/accordion/specs/NsCollapse.spec.tsx +37 -0
  13. package/src/common/accordion/specs/NsToggleSection.spec.tsx +38 -0
  14. package/src/common/accordion/stories/NsAccordion.stories.tsx +47 -0
  15. package/src/common/accordion/stories/NsAccordionHeader.stories.tsx +40 -0
  16. package/src/common/accordion/stories/NsAccordionItem.stories.tsx +40 -0
  17. package/src/common/accordion/stories/NsCollapse.stories.tsx +35 -0
  18. package/src/common/accordion/stories/NsToggleSection.stories.tsx +23 -0
  19. package/src/common/accordion/stories/data.tsx +22 -0
  20. package/src/common/accordion/types.d.ts +73 -0
  21. package/src/common/alert/NsAlert.spec.tsx +0 -1
  22. package/src/common/alert/NsAlert.styled.ts +10 -10
  23. package/src/common/alert/NsAlert.tsx +4 -3
  24. package/src/common/alert/types.d.ts +22 -22
  25. package/src/common/auth-dropdown/NsAuthDropdown.styled.ts +12 -11
  26. package/src/common/auth-dropdown/NsAuthDropdown.tsx +2 -1
  27. package/src/common/auth-dropdown/types.d.ts +3 -1
  28. package/src/common/autocomplete/NsAutocomplete.spec.tsx +20 -8
  29. package/src/common/autocomplete/NsAutocomplete.stories.tsx +3 -0
  30. package/src/common/autocomplete/NsAutocomplete.styled.tsx +12 -0
  31. package/src/common/autocomplete/NsAutocomplete.tsx +36 -18
  32. package/src/common/autocomplete/types.d.ts +7 -1
  33. package/src/common/brand-components/NsBrandComponent.tsx +3 -0
  34. package/src/common/breadcrumb/NsBreadcrumb.tsx +4 -2
  35. package/src/common/button/NsButton.spec.tsx +11 -12
  36. package/src/common/button/NsButton.stories.tsx +1 -1
  37. package/src/common/button/NsButton.styled.tsx +25 -23
  38. package/src/common/button/NsButton.tsx +9 -2
  39. package/src/common/button/types.d.ts +1 -0
  40. package/src/common/checkbox/NsCheckbox.styled.tsx +2 -2
  41. package/src/common/common.types.d.ts +2 -0
  42. package/src/common/dropdown/types.d.ts +3 -1
  43. package/src/common/email-simple/NsEmailSimple.spec.tsx +145 -0
  44. package/src/common/email-simple/NsEmailSimple.stories.tsx +61 -0
  45. package/src/common/email-simple/NsEmailSimple.tsx +71 -0
  46. package/src/common/email-simple/index.ts +2 -0
  47. package/src/common/email-simple/types.d.ts +14 -0
  48. package/src/common/flag/NsFlag.spec.tsx +4 -24
  49. package/src/common/flag/NsFlag.styled.tsx +5 -5
  50. package/src/common/flag/NsFlag.tsx +0 -2
  51. package/src/common/icon/types.d.ts +1 -0
  52. package/src/common/image/types.d.ts +1 -0
  53. package/src/common/index.ts +3 -0
  54. package/src/common/input/InputFieldWrapper.tsx +4 -4
  55. package/src/common/input/NsInputField.stories.tsx +11 -9
  56. package/src/common/input/NsInputField.tsx +12 -5
  57. package/src/common/input/getAdornmentProps.tsx +4 -0
  58. package/src/common/input/types.d.ts +3 -1
  59. package/src/common/label/NsLabel.stories.tsx +3 -0
  60. package/src/common/label/NsLabel.tsx +18 -17
  61. package/src/common/label/types.d.ts +2 -1
  62. package/src/common/nav-tab/NsNavTab.spec.tsx +0 -5
  63. package/src/common/nav-tab/NsNavTab.styled.tsx +7 -4
  64. package/src/common/radiogroup/NsRadioGroup.spec.tsx +64 -0
  65. package/src/common/radiogroup/NsRadioGroup.stories.tsx +102 -0
  66. package/src/common/radiogroup/NsRadioGroup.styled.tsx +50 -0
  67. package/src/common/radiogroup/NsRadioGroup.tsx +61 -0
  68. package/src/common/radiogroup/index.ts +2 -0
  69. package/src/common/radiogroup/types.d.ts +20 -0
  70. package/src/common/select/NsSelect.spec.tsx +200 -0
  71. package/src/common/select/NsSelect.styled.tsx +85 -0
  72. package/src/common/select/NsSelect.tsx +117 -0
  73. package/src/common/select/NsSelectOption.tsx +30 -0
  74. package/src/common/select/index.ts +3 -0
  75. package/src/common/select/stories/NsSelect.stories.tsx +134 -0
  76. package/src/common/select/types.d.ts +34 -0
  77. package/src/common/table/NsExpandableRow.tsx +68 -0
  78. package/src/common/table/NsTable.spec.tsx +69 -41
  79. package/src/common/table/NsTable.stories.tsx +45 -7
  80. package/src/common/table/NsTable.styled.ts +106 -12
  81. package/src/common/table/NsTable.tsx +78 -89
  82. package/src/common/table/NsTableHeader.tsx +52 -38
  83. package/src/common/table/NsTablePagination.tsx +21 -13
  84. package/src/common/table/types.d.ts +21 -8
  85. package/src/common/text-strip/NsTextStrip.tsx +10 -7
  86. package/src/common/typography/Typography.styled.tsx +3 -3
  87. package/src/common/validations/regex.ts +3 -0
  88. /package/src/common/card/{tests → specs}/BrandLogo.spec.tsx +0 -0
  89. /package/src/common/card/{tests → specs}/NsBgCard.spec.tsx +0 -0
  90. /package/src/common/card/{tests → specs}/NsBrandProductCard.spec.tsx +0 -0
  91. /package/src/common/card/{tests → specs}/NsFfgCard.spec.tsx +0 -0
  92. /package/src/common/carousal/{tests → specs}/Chevron.spec.tsx +0 -0
  93. /package/src/common/carousal/{tests → specs}/NsCarousal.spec.tsx +0 -0
  94. /package/src/common/carousal/{tests → specs}/NsScrollingCarousal.spec.tsx +0 -0
  95. /package/src/common/carousal/{tests → specs}/RenderBrand.spec.tsx +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/foundation-ui-components",
3
- "version": "1.0.0-develop.3",
3
+ "version": "1.0.0-develop.5",
4
4
  "description": "It is a foundation library written in react, that provides common components to all UI pages",
5
5
  "private": false,
6
6
  "main": "src/common/index.ts",
@@ -31,7 +31,10 @@
31
31
  "test:coverage": "yarn test --coverage",
32
32
  "test:watch": "jest --watch",
33
33
  "storybook": "storybook dev -p 6007",
34
- "build-storybook": "storybook build"
34
+ "build-storybook": "storybook build",
35
+ "export": "next export",
36
+ "prepare": "husky install",
37
+ "types:check": "tsc --pretty --noEmit"
35
38
  },
36
39
  "dependencies": {
37
40
  "@emotion/react": "^11.10.6",
@@ -39,8 +42,7 @@
39
42
  "@mui/icons-material": "^5.11.11",
40
43
  "@mui/material": "5.4.2",
41
44
  "@next/font": "13.2.4",
42
- "@nuskin/foundation-theme": "1.0.0-cft-33.12",
43
- "@types/jest": "^29.5.0",
45
+ "@nuskin/foundation-theme": "1.0.0-develop.5",
44
46
  "framer-motion": "^10.8.5",
45
47
  "next": "13.2.4",
46
48
  "react": "18.2.0",
@@ -69,8 +71,10 @@
69
71
  "@storybook/testing-library": "^0.1.0",
70
72
  "@testing-library/jest-dom": "^5.16.5",
71
73
  "@testing-library/react": "^14.0.0",
74
+ "@types/jest": "^29.5.0",
72
75
  "@types/lodash": "^4.14.191",
73
76
  "@types/node": "18.15.0",
77
+ "@types/ramda": "^0.29.3",
74
78
  "@types/react": "18.0.28",
75
79
  "@types/react-dom": "18.0.11",
76
80
  "@types/react-slick": "^0.23.10",
@@ -86,6 +90,7 @@
86
90
  "eslint-plugin-json": "^3.1.0",
87
91
  "eslint-plugin-prettier": "^4.2.1",
88
92
  "eslint-plugin-storybook": "^0.6.11",
93
+ "husky": "^8.0.0",
89
94
  "jest": "^29.5.0",
90
95
  "jest-environment-jsdom": "^29.5.0",
91
96
  "storybook": "^7.0.7"
@@ -0,0 +1,77 @@
1
+ import { useState, useEffect, useMemo } from 'react';
2
+ import NsAccordionItem from './NsAccordionItem';
3
+ import { NsAccordionProps } from './types';
4
+ import { StyledAccordionWrapper } from './ToggleSection.styled';
5
+
6
+ /**
7
+ * NsAccordion can be used to show a list of items with title and body
8
+ * it accepts a `boolean` prop `allowMultipleExpanded` which when true,
9
+ * multiple sections of accordion can remain open at a time
10
+ *
11
+ * we can define certain section to remain open by default using `isDefaultOpen` in the
12
+ * contentArray item
13
+ * if there are multiple items available for default open, but `allowMultipleExpanded`
14
+ * is false then only first item of default open is made open
15
+ */
16
+ const NsAccordion = ({
17
+ additionalProps,
18
+ allowMultipleExpanded,
19
+ contentArray,
20
+ variant,
21
+ headerTypographyVariant,
22
+ closedIconName,
23
+ openedIconName,
24
+ }: NsAccordionProps) => {
25
+ const defaultOpenIds = useMemo(() => {
26
+ return contentArray
27
+ .map((content, index) => (content?.isDefaultOpen ? index : null))
28
+ .filter((e) => typeof e === 'number') as number[];
29
+ }, [contentArray]);
30
+ const [expandedIds, setExpandedIds] = useState<Array<number>>(defaultOpenIds);
31
+
32
+ const toggleOpen = (sectionNumber: number, isExpanded: boolean) => {
33
+ if (allowMultipleExpanded) {
34
+ if (isExpanded) {
35
+ setExpandedIds(expandedIds.filter((existingNumber) => existingNumber !== sectionNumber));
36
+ } else {
37
+ setExpandedIds([...expandedIds, sectionNumber]);
38
+ }
39
+ } else {
40
+ if (isExpanded) {
41
+ setExpandedIds([]);
42
+ } else {
43
+ setExpandedIds([sectionNumber]);
44
+ }
45
+ }
46
+ };
47
+ useEffect(() => {
48
+ setExpandedIds(allowMultipleExpanded ? defaultOpenIds : [defaultOpenIds[0]]);
49
+ }, [allowMultipleExpanded, defaultOpenIds]);
50
+
51
+ return (
52
+ <StyledAccordionWrapper
53
+ variant={variant}
54
+ className="ns-accordion"
55
+ data-testid={additionalProps?.testid ?? 'ns-accordion-testid'}
56
+ >
57
+ {contentArray.map((content, index) => {
58
+ const isOpen = expandedIds.includes(index);
59
+ return (
60
+ <NsAccordionItem
61
+ variant={variant}
62
+ headerTypographyVariant={headerTypographyVariant}
63
+ closedIconName={closedIconName}
64
+ openedIconName={openedIconName}
65
+ key={index}
66
+ isOpen={isOpen}
67
+ toggleOpen={() => toggleOpen(index, isOpen)}
68
+ content={content?.body}
69
+ title={content?.title}
70
+ />
71
+ );
72
+ })}
73
+ </StyledAccordionWrapper>
74
+ );
75
+ };
76
+
77
+ export default NsAccordion;
@@ -0,0 +1,56 @@
1
+ import { NsTypography, NsIcon, NsAccordionHeaderProps, AccordionVariant } from '..';
2
+ import { styled, ColorUtils } from '@nuskin/foundation-theme';
3
+ const { getPaletteColor } = ColorUtils;
4
+
5
+ const StyledHeader = styled('header')<{ variant: AccordionVariant }>(({ theme, variant }) => {
6
+ const backgroundColor = getPaletteColor(theme, 'primary', 'contrastText') as string;
7
+ let borderBottom = 'none';
8
+ if (variant === AccordionVariant.Compact) {
9
+ borderBottom = '1px solid rgb(0,0,0,0.125)';
10
+ }
11
+ return {
12
+ backgroundColor,
13
+ cursor: 'pointer',
14
+ display: 'grid',
15
+ gridTemplateColumns: '1fr 24px',
16
+ padding: '9px 16px',
17
+ borderBottom,
18
+ } as any;
19
+ });
20
+
21
+ /**
22
+ * NsAccordionHeader is primarily used by NsToggleSection and NsAccordionItem
23
+ * as a header, so it accepts `title` , `isOpen` and `onClick`
24
+ */
25
+ const NsAccordionHeader = ({
26
+ title,
27
+ onClick,
28
+ isOpen,
29
+ additionalProps,
30
+ openedIconName,
31
+ closedIconName,
32
+ variant,
33
+ headerTypographyVariant,
34
+ }: NsAccordionHeaderProps) => {
35
+ return (
36
+ <StyledHeader
37
+ role="button"
38
+ aria-expanded={isOpen ? 'true' : 'false'}
39
+ className={`section-header ${isOpen ? 'expanded' : ''} ${additionalProps?.className ?? ''}`}
40
+ onClick={onClick}
41
+ variant={variant}
42
+ >
43
+ {typeof title === 'string' ? (
44
+ <NsTypography variant={headerTypographyVariant ?? 'h4'} weight="bold">
45
+ {title}
46
+ </NsTypography>
47
+ ) : (
48
+ title
49
+ )}
50
+ <span className="header-icon">
51
+ <NsIcon name={isOpen ? openedIconName ?? 'arrowUp' : closedIconName ?? 'arrowDown'} />
52
+ </span>
53
+ </StyledHeader>
54
+ );
55
+ };
56
+ export default NsAccordionHeader;
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { NsAccordionItemProps, NsCollapse } from '..';
3
+ import NsAccordionHeader from './NsAccordionHeader';
4
+ import { StyledToggleSection } from './ToggleSection.styled';
5
+
6
+ /**
7
+ * this component is used by accordion to pass title
8
+ * and content
9
+ */
10
+ const NsAccordionItem = ({
11
+ isOpen,
12
+ toggleOpen,
13
+ title,
14
+ content,
15
+ variant,
16
+ headerTypographyVariant,
17
+ closedIconName,
18
+ openedIconName,
19
+ }: NsAccordionItemProps) => {
20
+ return (
21
+ <StyledToggleSection variant={variant}>
22
+ <NsAccordionHeader
23
+ additionalProps={{ className: 'accordion-section' }}
24
+ closedIconName={closedIconName}
25
+ headerTypographyVariant={headerTypographyVariant}
26
+ isOpen={isOpen}
27
+ openedIconName={openedIconName}
28
+ onClick={toggleOpen}
29
+ title={title}
30
+ variant={variant}
31
+ />
32
+ <NsCollapse isOpen={isOpen} variant={variant}>
33
+ {content}
34
+ </NsCollapse>
35
+ </StyledToggleSection>
36
+ );
37
+ };
38
+
39
+ export default NsAccordionItem;
@@ -0,0 +1,56 @@
1
+ import { styled, ColorUtils, FoundationTheme } from '@nuskin/foundation-theme';
2
+ import { AccordionVariant, NsCollapseProps } from './types.d';
3
+ const { getAppColor, getPaletteColor } = ColorUtils;
4
+ const getIsLight = (theme: FoundationTheme.Theme) => {
5
+ const { palette = { primary: { main: '', contrastText: '' } } } = theme;
6
+ return palette.primary.main === getAppColor(theme, 'primary', 'black');
7
+ };
8
+ const options = {
9
+ shouldForwardProp: (key: string) => key !== 'variant',
10
+ };
11
+ export const StyledCollapse = styled(
12
+ 'section',
13
+ options
14
+ )<{ variant: AccordionVariant }>(({ theme, variant }) => {
15
+ let backgroundColor = getIsLight(theme)
16
+ ? (getAppColor(theme, 'primary', 'gray') as string)
17
+ : (getAppColor(theme, 'primary', 'dark') as string);
18
+ let borderBottom = 'none';
19
+ if (variant === AccordionVariant.Compact) {
20
+ backgroundColor = getIsLight(theme)
21
+ ? (getAppColor(theme, 'primary', 'white') as string)
22
+ : (getAppColor(theme, 'primary', 'dark') as string);
23
+ borderBottom = '1px solid rgb(0,0,0,0.125)';
24
+ }
25
+ return {
26
+ backgroundColor,
27
+ color: getPaletteColor(theme, 'primary', 'main') as string,
28
+ opacity: 0,
29
+ padding: '0 16px',
30
+ height: 0,
31
+ overflow: 'hidden',
32
+ transition: '0.3s',
33
+ '&.expanded': {
34
+ padding: '9px 16px',
35
+ height: 'auto',
36
+ opacity: 1,
37
+ borderBottom,
38
+ },
39
+ };
40
+ });
41
+
42
+ /**
43
+ * NsCollapse component adds css animation while showing children items if prop `isOpen` is true,
44
+ * and while removing children if prop `isOpen` is false
45
+ */
46
+ const NsCollapse = ({ isOpen, children, additionalProps, variant }: NsCollapseProps) => {
47
+ return (
48
+ <StyledCollapse
49
+ className={`ns-collapse ${isOpen ? 'expanded' : ''} ${additionalProps?.className ?? ''}`}
50
+ variant={variant}
51
+ >
52
+ {isOpen ? children : null}
53
+ </StyledCollapse>
54
+ );
55
+ };
56
+ export default NsCollapse;
@@ -0,0 +1,30 @@
1
+ import React, { useState } from 'react';
2
+ import { NsToggleSectionProps, NsAccordionItem } from '../';
3
+
4
+ /**
5
+ * NsToggleSection takes prop `title` and prop `children` and toggles
6
+ * children with animation when clicked on title
7
+ *
8
+ * it also has a `defaultOpen` prop, which when true, children is visible
9
+ * by default, otherwise children is hidden by default and toggles between visible
10
+ * and hidden on clicking title
11
+ */
12
+ const NsToggleSection = ({ title, children, additionalProps, defaultOpen, variant }: NsToggleSectionProps) => {
13
+ const [isExpanded, setIsExpanded] = useState(defaultOpen ?? false);
14
+
15
+ const toggleAccordion = () => {
16
+ setIsExpanded(!isExpanded);
17
+ };
18
+ return (
19
+ <NsAccordionItem
20
+ variant={variant}
21
+ additionalProps={{ ...additionalProps, className: 'toggle-section ' + additionalProps?.className ?? '' }}
22
+ isOpen={isExpanded}
23
+ title={title}
24
+ content={children}
25
+ toggleOpen={toggleAccordion}
26
+ />
27
+ );
28
+ };
29
+
30
+ export default NsToggleSection;
@@ -0,0 +1,28 @@
1
+ import { styled } from '@nuskin/foundation-theme';
2
+ import { AccordionVariant } from './types.d';
3
+
4
+ export const StyledToggleSection = styled('section')<{ variant: AccordionVariant }>(({ variant }) => {
5
+ if (variant === AccordionVariant.Compact) {
6
+ return { borderRight: '1px solid rgb(0,0,0,0.125)' };
7
+ }
8
+ return {
9
+ margin: '5px',
10
+ };
11
+ });
12
+
13
+ export const StyledAccordionWrapper = styled('div')<{ variant: AccordionVariant }>(({ variant }) => {
14
+ if (variant === AccordionVariant.Compact) {
15
+ return {
16
+ display: 'flex',
17
+ flexDirection: 'column',
18
+ gap: '0',
19
+ borderTop: '1px solid rgb(0,0,0,0.125)',
20
+ borderLeft: '1px solid rgb(0,0,0,0.125)',
21
+ };
22
+ }
23
+ return {
24
+ display: 'flex',
25
+ flexDirection: 'column',
26
+ gap: '10px',
27
+ };
28
+ });
@@ -0,0 +1,6 @@
1
+ export { default as NsCollapse } from './NsCollapse';
2
+ export { default as NsAccordionHeader } from './NsAccordionHeader';
3
+ export { default as NsToggleSection } from './NsToggleSection';
4
+ export { default as NsAccordionItem } from './NsAccordionItem';
5
+ export { default as NsAccordion } from './NsAccordion';
6
+ export * from './types.d';
@@ -0,0 +1,93 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen } from '@/test-utils/index';
3
+ import { AccordionVariant, NsAccordion, NsAccordionProps } from '@/common/index';
4
+
5
+ const generateContentArray = (count: number) => {
6
+ const arr = [] as NsAccordionProps['contentArray'];
7
+ for (let i = 1; i <= count; i++) {
8
+ arr.push({
9
+ title: `Title ${i}`,
10
+ body: `Content ${i}`,
11
+ isDefaultOpen: true,
12
+ });
13
+ }
14
+ return arr;
15
+ };
16
+
17
+ describe('NsAccordion', () => {
18
+ const contentArray = generateContentArray(3);
19
+ const defaultProps = {
20
+ additionalProps: {
21
+ testid: 'test-accordion',
22
+ },
23
+ allowMultipleExpanded: false,
24
+ contentArray,
25
+ variant: AccordionVariant.Basic,
26
+ };
27
+
28
+ it('renders without error', () => {
29
+ const { getByTestId } = render(<NsAccordion {...defaultProps} />);
30
+ const accordion = getByTestId(defaultProps.additionalProps.testid);
31
+ expect(accordion).toBeInTheDocument();
32
+ });
33
+
34
+ it(`only first section should be visible when allowMultipleExpanded prop is false and multiple items have
35
+ isDefaultOpen true`, () => {
36
+ const { getAllByText } = render(<NsAccordion {...defaultProps} />);
37
+ const [title1, title2, title3] = getAllByText(/Title /i);
38
+
39
+ expect(title1.closest('header')).toHaveAttribute('aria-expanded', 'true');
40
+ expect(title2.closest('header')).toHaveAttribute('aria-expanded', 'false');
41
+ expect(title3.closest('header')).toHaveAttribute('aria-expanded', 'false');
42
+ expect(screen.queryByText(/Content 1/i)).not.toBeNull();
43
+ expect(screen.queryByText(/Content 2/i)).toBeNull();
44
+ expect(screen.queryByText(/Content 3/i)).toBeNull();
45
+ });
46
+
47
+ it('expands second section only when allowMultipleExpanded prop is false and second title is clicked', async () => {
48
+ const { getAllByText } = render(<NsAccordion {...defaultProps} />);
49
+ const [title1, title2, title3] = getAllByText(/Title /i);
50
+ const header = title2.closest('header');
51
+ expect(header).toHaveAttribute('aria-expanded', 'false');
52
+ if (header) {
53
+ fireEvent.click(header);
54
+ expect(title1.closest('header')).toHaveAttribute('aria-expanded', 'false');
55
+ expect(title2.closest('header')).toHaveAttribute('aria-expanded', 'true');
56
+ expect(title3.closest('header')).toHaveAttribute('aria-expanded', 'false');
57
+ expect(screen.queryByText(/Content 1/i)).toBeNull();
58
+ expect(screen.queryByText(/Content 2/i)).not.toBeNull();
59
+ expect(screen.queryByText(/Content 3/i)).toBeNull();
60
+ }
61
+ });
62
+
63
+ it('expands multiple sections by default when allowMultipleExpanded prop is true', () => {
64
+ const contentArray = generateContentArray(3);
65
+ contentArray[2].isDefaultOpen = false;
66
+ const defaultProps = {
67
+ additionalProps: {
68
+ testid: 'test-accordion',
69
+ },
70
+ allowMultipleExpanded: false,
71
+ contentArray,
72
+ variant: AccordionVariant.Basic,
73
+ };
74
+ const { getAllByText } = render(<NsAccordion {...defaultProps} allowMultipleExpanded />);
75
+ const [title1, title2, title3] = getAllByText(/Title /i);
76
+
77
+ expect(title1.closest('header')).toHaveAttribute('aria-expanded', 'true');
78
+ expect(title2.closest('header')).toHaveAttribute('aria-expanded', 'true');
79
+ expect(title3.closest('header')).toHaveAttribute('aria-expanded', 'false');
80
+ expect(screen.queryByText(/Content 1/i)).not.toBeNull();
81
+ expect(screen.queryByText(/Content 2/i)).not.toBeNull();
82
+ expect(screen.queryByText(/Content 3/i)).toBeNull();
83
+
84
+ fireEvent.click(title3);
85
+
86
+ expect(title1.closest('header')).toHaveAttribute('aria-expanded', 'true');
87
+ expect(title2.closest('header')).toHaveAttribute('aria-expanded', 'true');
88
+ expect(title3.closest('header')).toHaveAttribute('aria-expanded', 'true');
89
+ expect(screen.queryByText(/Content 1/i)).not.toBeNull();
90
+ expect(screen.queryByText(/Content 2/i)).not.toBeNull();
91
+ expect(screen.queryByText(/Content 3/i)).not.toBeNull();
92
+ });
93
+ });
@@ -0,0 +1,63 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@/test-utils/index';
3
+ import { NsAccordionHeader, AccordionVariant } from '@/common/index';
4
+
5
+ describe('NsAccordionHeader', () => {
6
+ it('renders title correctly', () => {
7
+ const onClick = jest.fn();
8
+ const title = 'Section Header';
9
+ const { getByText } = render(
10
+ <NsAccordionHeader
11
+ variant={AccordionVariant.Basic}
12
+ title={title}
13
+ onClick={onClick}
14
+ />
15
+ );
16
+ expect(getByText(title)).toBeInTheDocument();
17
+ });
18
+
19
+ it('calls onClick handler when clicked', () => {
20
+ const onClick = jest.fn();
21
+ const { container } = render(
22
+ <NsAccordionHeader
23
+ variant={AccordionVariant.Basic}
24
+ title="Section Header"
25
+ onClick={onClick}
26
+ />);
27
+ const header = container.querySelector('header');
28
+ if (header) {
29
+ fireEvent.click(header);
30
+ expect(onClick).toHaveBeenCalledTimes(1);
31
+ }
32
+ });
33
+
34
+ it('renders with additional class name', () => {
35
+ const additionalClassName = 'custom-class';
36
+ const onClick = jest.fn();
37
+ const { container } = render(
38
+ <NsAccordionHeader
39
+ variant={AccordionVariant.Basic}
40
+ title="Section Header"
41
+ additionalProps={{ className: additionalClassName }}
42
+ onClick={onClick}
43
+ />
44
+ );
45
+ expect(container.querySelector('header')).toHaveClass(`section-header ${additionalClassName}`);
46
+ });
47
+
48
+ it('renders with expanded class when isOpen is true', () => {
49
+ const onClick = jest.fn();
50
+ const { container } = render(
51
+ <NsAccordionHeader variant={AccordionVariant.Basic} title="Section Header" isOpen onClick={onClick} />
52
+ );
53
+ expect(container.querySelector('header')).toHaveClass('expanded');
54
+ });
55
+
56
+ it('renders with header icon', () => {
57
+ const onClick = jest.fn();
58
+ const { getByTestId } = render(
59
+ <NsAccordionHeader variant={AccordionVariant.Basic} title="Section Header" onClick={onClick} />
60
+ );
61
+ expect(getByTestId('icon')).toBeInTheDocument();
62
+ });
63
+ });
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@/test-utils/index';
3
+ import { NsAccordionItem, AccordionVariant } from '@/common/index';
4
+
5
+ describe('NsAccordionItem', () => {
6
+ const title = 'Accordion Section Title';
7
+ const content = 'Accordion Section Content';
8
+
9
+ it('renders title correctly', () => {
10
+ const { getByText } = render(
11
+ <NsAccordionItem
12
+ variant={AccordionVariant.Basic}
13
+ isOpen={false}
14
+ toggleOpen={() => {}}
15
+ title={title}
16
+ content={content}
17
+ />
18
+ );
19
+ expect(getByText(title)).toBeInTheDocument();
20
+ });
21
+
22
+ it('calls toggleOpen function when clicked on the section header', () => {
23
+ const toggleOpen = jest.fn();
24
+ const { getByText } = render(
25
+ <NsAccordionItem
26
+ variant={AccordionVariant.Basic}
27
+ isOpen={false}
28
+ toggleOpen={toggleOpen}
29
+ title={title}
30
+ content={content}
31
+ />
32
+ );
33
+ fireEvent.click(getByText(title));
34
+ expect(toggleOpen).toHaveBeenCalledTimes(1);
35
+ });
36
+
37
+ it('renders content when isOpen is true', () => {
38
+ const { getByText } = render(
39
+ <NsAccordionItem
40
+ variant={AccordionVariant.Basic}
41
+ isOpen={true}
42
+ toggleOpen={() => {}}
43
+ title={title}
44
+ content={content}
45
+ />
46
+ );
47
+ expect(getByText(content)).toBeInTheDocument();
48
+ });
49
+
50
+ it('does not render content when isOpen is false', () => {
51
+ const { queryByText } = render(
52
+ <NsAccordionItem
53
+ variant={AccordionVariant.Basic}
54
+ isOpen={false}
55
+ toggleOpen={() => {}}
56
+ title={title}
57
+ content={content}
58
+ />
59
+ );
60
+ expect(queryByText(content)).toBeNull();
61
+ });
62
+ });
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@/test-utils/index';
3
+ import { NsCollapse, AccordionVariant } from '@/common/index';
4
+
5
+ describe('NsCollapse', () => {
6
+ it('renders children when isOpen is true', () => {
7
+ const { getByText } = render(
8
+ <NsCollapse isOpen variant={AccordionVariant.Basic}>
9
+ <div>Child 1</div>
10
+ <div>Child 2</div>
11
+ </NsCollapse>
12
+ );
13
+ expect(getByText('Child 1')).toBeInTheDocument();
14
+ expect(getByText('Child 2')).toBeInTheDocument();
15
+ });
16
+
17
+ it('does not render children when isOpen is false', () => {
18
+ render(
19
+ <NsCollapse isOpen={false} variant={AccordionVariant.Basic}>
20
+ <div>Child 1</div>
21
+ <div>Child 2</div>
22
+ </NsCollapse>
23
+ );
24
+ expect(screen.queryByText('Child')).toBeNull();
25
+ });
26
+
27
+ it('renders with additional class name', () => {
28
+ const additionalClassName = 'custom-class';
29
+ const { container } = render(
30
+ <NsCollapse additionalProps={{ className: additionalClassName }} isOpen variant={AccordionVariant.Basic}>
31
+ <div>Child 1</div>
32
+ <div>Child 2</div>
33
+ </NsCollapse>
34
+ );
35
+ expect(container.querySelector('section')).toHaveClass(`ns-collapse ${additionalClassName}`);
36
+ });
37
+ });
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+
3
+ import { AccordionVariant, NsToggleSection } from '@/common/index';
4
+
5
+ import { render, fireEvent } from '@/test-utils/index';
6
+
7
+ describe('NsToggleSection', () => {
8
+ const title = 'Toggle Section Title';
9
+ const content = 'Toggle Section Content';
10
+
11
+ it('renders title correctly', () => {
12
+ const { getByText } = render(
13
+ <NsToggleSection variant={AccordionVariant.Basic} title={title}>{content}</NsToggleSection>
14
+ );
15
+ expect(getByText(title)).toBeInTheDocument();
16
+ });
17
+
18
+ it('toggles content visibility when clicked on the section header', () => {
19
+ const { getByText, queryByText } = render(
20
+ <NsToggleSection variant={AccordionVariant.Basic} title={title}>{content}</NsToggleSection>
21
+ );
22
+ expect(queryByText(content)).toBeNull();
23
+ fireEvent.click(getByText(title));
24
+ expect(getByText(content)).toBeInTheDocument();
25
+ fireEvent.click(getByText(title));
26
+ expect(queryByText(content)).toBeNull();
27
+ });
28
+
29
+ it('sets defaultOpen prop correctly', () => {
30
+ const { getByText } = render(
31
+ <NsToggleSection variant={AccordionVariant.Basic} title={title} defaultOpen>
32
+ {content}
33
+ </NsToggleSection>
34
+ );
35
+
36
+ expect(getByText(content)).toBeInTheDocument();
37
+ });
38
+ });