@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.
- package/package.json +9 -4
- package/src/common/accordion/NsAccordion.tsx +77 -0
- package/src/common/accordion/NsAccordionHeader.tsx +56 -0
- package/src/common/accordion/NsAccordionItem.tsx +39 -0
- package/src/common/accordion/NsCollapse.tsx +56 -0
- package/src/common/accordion/NsToggleSection.tsx +30 -0
- package/src/common/accordion/ToggleSection.styled.tsx +28 -0
- package/src/common/accordion/index.ts +6 -0
- package/src/common/accordion/specs/NsAccordion.spec.tsx +93 -0
- package/src/common/accordion/specs/NsAccordionHeader.spec.tsx +63 -0
- package/src/common/accordion/specs/NsAccordionItem.spec.tsx +62 -0
- package/src/common/accordion/specs/NsCollapse.spec.tsx +37 -0
- package/src/common/accordion/specs/NsToggleSection.spec.tsx +38 -0
- package/src/common/accordion/stories/NsAccordion.stories.tsx +47 -0
- package/src/common/accordion/stories/NsAccordionHeader.stories.tsx +40 -0
- package/src/common/accordion/stories/NsAccordionItem.stories.tsx +40 -0
- package/src/common/accordion/stories/NsCollapse.stories.tsx +35 -0
- package/src/common/accordion/stories/NsToggleSection.stories.tsx +23 -0
- package/src/common/accordion/stories/data.tsx +22 -0
- package/src/common/accordion/types.d.ts +73 -0
- package/src/common/alert/NsAlert.spec.tsx +0 -1
- package/src/common/alert/NsAlert.styled.ts +10 -10
- package/src/common/alert/NsAlert.tsx +4 -3
- package/src/common/alert/types.d.ts +22 -22
- package/src/common/auth-dropdown/NsAuthDropdown.styled.ts +12 -11
- package/src/common/auth-dropdown/NsAuthDropdown.tsx +2 -1
- package/src/common/auth-dropdown/types.d.ts +3 -1
- package/src/common/autocomplete/NsAutocomplete.spec.tsx +20 -8
- package/src/common/autocomplete/NsAutocomplete.stories.tsx +3 -0
- package/src/common/autocomplete/NsAutocomplete.styled.tsx +12 -0
- package/src/common/autocomplete/NsAutocomplete.tsx +36 -18
- package/src/common/autocomplete/types.d.ts +7 -1
- package/src/common/brand-components/NsBrandComponent.tsx +3 -0
- package/src/common/breadcrumb/NsBreadcrumb.tsx +4 -2
- package/src/common/button/NsButton.spec.tsx +11 -12
- package/src/common/button/NsButton.stories.tsx +1 -1
- package/src/common/button/NsButton.styled.tsx +25 -23
- package/src/common/button/NsButton.tsx +9 -2
- package/src/common/button/types.d.ts +1 -0
- package/src/common/checkbox/NsCheckbox.styled.tsx +2 -2
- package/src/common/common.types.d.ts +2 -0
- package/src/common/dropdown/types.d.ts +3 -1
- package/src/common/email-simple/NsEmailSimple.spec.tsx +145 -0
- package/src/common/email-simple/NsEmailSimple.stories.tsx +61 -0
- package/src/common/email-simple/NsEmailSimple.tsx +71 -0
- package/src/common/email-simple/index.ts +2 -0
- package/src/common/email-simple/types.d.ts +14 -0
- package/src/common/flag/NsFlag.spec.tsx +4 -24
- package/src/common/flag/NsFlag.styled.tsx +5 -5
- package/src/common/flag/NsFlag.tsx +0 -2
- package/src/common/icon/types.d.ts +1 -0
- package/src/common/image/types.d.ts +1 -0
- package/src/common/index.ts +3 -0
- package/src/common/input/InputFieldWrapper.tsx +4 -4
- package/src/common/input/NsInputField.stories.tsx +11 -9
- package/src/common/input/NsInputField.tsx +12 -5
- package/src/common/input/getAdornmentProps.tsx +4 -0
- package/src/common/input/types.d.ts +3 -1
- package/src/common/label/NsLabel.stories.tsx +3 -0
- package/src/common/label/NsLabel.tsx +18 -17
- package/src/common/label/types.d.ts +2 -1
- package/src/common/nav-tab/NsNavTab.spec.tsx +0 -5
- package/src/common/nav-tab/NsNavTab.styled.tsx +7 -4
- package/src/common/radiogroup/NsRadioGroup.spec.tsx +64 -0
- package/src/common/radiogroup/NsRadioGroup.stories.tsx +102 -0
- package/src/common/radiogroup/NsRadioGroup.styled.tsx +50 -0
- package/src/common/radiogroup/NsRadioGroup.tsx +61 -0
- package/src/common/radiogroup/index.ts +2 -0
- package/src/common/radiogroup/types.d.ts +20 -0
- package/src/common/select/NsSelect.spec.tsx +200 -0
- package/src/common/select/NsSelect.styled.tsx +85 -0
- package/src/common/select/NsSelect.tsx +117 -0
- package/src/common/select/NsSelectOption.tsx +30 -0
- package/src/common/select/index.ts +3 -0
- package/src/common/select/stories/NsSelect.stories.tsx +134 -0
- package/src/common/select/types.d.ts +34 -0
- package/src/common/table/NsExpandableRow.tsx +68 -0
- package/src/common/table/NsTable.spec.tsx +69 -41
- package/src/common/table/NsTable.stories.tsx +45 -7
- package/src/common/table/NsTable.styled.ts +106 -12
- package/src/common/table/NsTable.tsx +78 -89
- package/src/common/table/NsTableHeader.tsx +52 -38
- package/src/common/table/NsTablePagination.tsx +21 -13
- package/src/common/table/types.d.ts +21 -8
- package/src/common/text-strip/NsTextStrip.tsx +10 -7
- package/src/common/typography/Typography.styled.tsx +3 -3
- package/src/common/validations/regex.ts +3 -0
- /package/src/common/card/{tests → specs}/BrandLogo.spec.tsx +0 -0
- /package/src/common/card/{tests → specs}/NsBgCard.spec.tsx +0 -0
- /package/src/common/card/{tests → specs}/NsBrandProductCard.spec.tsx +0 -0
- /package/src/common/card/{tests → specs}/NsFfgCard.spec.tsx +0 -0
- /package/src/common/carousal/{tests → specs}/Chevron.spec.tsx +0 -0
- /package/src/common/carousal/{tests → specs}/NsCarousal.spec.tsx +0 -0
- /package/src/common/carousal/{tests → specs}/NsScrollingCarousal.spec.tsx +0 -0
- /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
|
+
"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-
|
|
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
|
+
});
|