Skip to content

Commit 5e8e0f1

Browse files
authored
Merge pull request #31 from kubit-ui/feature/new-components-and-improvements
Feature/new components and improvements
2 parents 72ad973 + 80ddd4a commit 5e8e0f1

File tree

203 files changed

+3280
-805
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

203 files changed

+3280
-805
lines changed

package.json

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@kubit-ui-web/react-components",
3-
"version": "1.13.2",
3+
"version": "1.14.0",
44
"description": "Kubit React Components is a customizable, accessible library of React web components, designed to enhance your application's user experience",
55
"author": {
66
"name": "Kubit",
@@ -86,60 +86,60 @@
8686
"sideEffects": false,
8787
"license": "Apache-2.0",
8888
"devDependencies": {
89-
"@babel/core": "^7.25.2",
90-
"@babel/preset-env": "^7.25.4",
91-
"@babel/preset-react": "^7.24.7",
92-
"@babel/preset-typescript": "^7.24.7",
93-
"@eslint/compat": "^1.1.1",
89+
"@babel/core": "^7.25.8",
90+
"@babel/preset-env": "^7.25.8",
91+
"@babel/preset-react": "^7.25.7",
92+
"@babel/preset-typescript": "^7.25.7",
93+
"@eslint/compat": "^1.2.0",
9494
"@eslint/eslintrc": "^3.1.0",
95-
"@eslint/js": "^9.11.1",
96-
"@storybook/addon-a11y": "^8.3.2",
97-
"@storybook/addon-controls": "^8.3.2",
95+
"@eslint/js": "^9.12.0",
96+
"@storybook/addon-a11y": "^8.3.5",
97+
"@storybook/addon-controls": "^8.3.5",
9898
"@storybook/addon-coverage": "^1.0.4",
99-
"@storybook/addon-essentials": "^8.3.2",
100-
"@storybook/addon-interactions": "^8.3.2",
101-
"@storybook/addon-links": "^8.3.2",
102-
"@storybook/addon-themes": "^8.3.2",
103-
"@storybook/blocks": "^8.3.2",
104-
"@storybook/react": "^8.3.2",
105-
"@storybook/react-vite": "^8.3.2",
99+
"@storybook/addon-essentials": "^8.3.5",
100+
"@storybook/addon-interactions": "^8.3.5",
101+
"@storybook/addon-links": "^8.3.5",
102+
"@storybook/addon-themes": "^8.3.5",
103+
"@storybook/blocks": "^8.3.5",
104+
"@storybook/react": "^8.3.5",
105+
"@storybook/react-vite": "^8.3.5",
106106
"@testing-library/jest-dom": "^6.5.0",
107107
"@testing-library/react": "^16.0.1",
108108
"@testing-library/react-hooks": "^8.0.1",
109109
"@testing-library/user-event": "^14.5.2",
110110
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
111111
"@types/jest": "^29.5.13",
112112
"@types/jest-axe": "^3.5.9",
113-
"@types/mocha": "^10.0.8",
114-
"@types/react": "^18.3.8",
113+
"@types/mocha": "^10.0.9",
114+
"@types/react": "^18.3.11",
115115
"@types/react-dom": "^18.3.0",
116116
"@types/styled-components": "^5.1.34",
117117
"@types/ungap__structured-clone": "^1.2.0",
118-
"@typescript-eslint/eslint-plugin": "^8.7.0",
119-
"@typescript-eslint/parser": "^8.7.0",
118+
"@typescript-eslint/eslint-plugin": "^8.8.1",
119+
"@typescript-eslint/parser": "^8.8.1",
120120
"@ungap/structured-clone": "^1.2.0",
121-
"@vitejs/plugin-react": "^4.3.1",
121+
"@vitejs/plugin-react": "^4.3.2",
122122
"babel-jest": "^29.7.0",
123123
"cpy-cli": "^5.0.0",
124-
"eslint": "^9.11.1",
124+
"eslint": "^9.12.0",
125125
"eslint-config-prettier": "^9.1.0",
126126
"eslint-config-standard-with-typescript": "^43.0.1",
127127
"eslint-import-resolver-typescript": "^3.6.3",
128-
"eslint-plugin-import": "^2.30.0",
128+
"eslint-plugin-import": "^2.31.0",
129129
"eslint-plugin-jest": "^28.8.3",
130130
"eslint-plugin-jsx-a11y": "^6.10.0",
131-
"eslint-plugin-n": "^17.10.3",
131+
"eslint-plugin-n": "^17.11.1",
132132
"eslint-plugin-node": "^11.1.0",
133133
"eslint-plugin-prettier": "5.2.1",
134134
"eslint-plugin-promise": "^7.1.0",
135-
"eslint-plugin-react": "^7.36.1",
135+
"eslint-plugin-react": "^7.37.1",
136136
"eslint-plugin-react-hooks": "^4.6.2",
137137
"eslint-plugin-react-refresh": "^0.4.12",
138-
"eslint-plugin-storybook": "^0.8.0",
138+
"eslint-plugin-storybook": "^0.9.0",
139139
"eslint-plugin-unused-imports": "^4.1.4",
140-
"globals": "^15.9.0",
140+
"globals": "^15.11.0",
141141
"gts": "^5.3.1",
142-
"html-validate": "^8.23.0",
142+
"html-validate": "^8.24.1",
143143
"jest": "^29.7.0",
144144
"jest-axe": "^9.0.0",
145145
"jest-canvas-mock": "^2.5.2",
@@ -151,13 +151,12 @@
151151
"prettier": "^3.3.3",
152152
"react-transition-group": "^4.4.5",
153153
"sort-imports": "^1.1.0",
154-
"storybook": "^8.3.2",
154+
"storybook": "^8.3.5",
155155
"ts-jest": "^29.2.5",
156156
"tsc-alias": "1.8.10",
157-
"typedoc": "^0.26.7",
158-
"typedoc-plugin-markdown": "^4.2.8",
159-
"typescript": "^5.6.2",
160-
"vite": "^5.4.7",
157+
"typedoc": "^0.26.8",
158+
"typescript": "^5.6.3",
159+
"vite": "^5.4.8",
161160
"vite-tsconfig-paths": "^4.3.2",
162161
"yarn-deduplicate": "^6.0.2"
163162
},

src/components/actionBottomSheet/__tests__/actionBottomSheet.test.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@ import * as React from 'react';
44
import { axe } from 'jest-axe';
55

66
import { renderProvider } from '@/tests/renderProvider/renderProvider.utility';
7-
import { windowMatchMedia } from '@/tests/windowMatchMedia';
87

98
import { ActionBottomSheetUnControlled as ActionBottomSheet } from '../actionBottomSheetUnControlled';
109

11-
window.matchMedia = windowMatchMedia();
12-
1310
describe('ActionBottomSheet component', () => {
1411
it('Should render ActionBottomSheet component', async () => {
1512
const { container } = renderProvider(
@@ -38,6 +35,30 @@ describe('ActionBottomSheet component', () => {
3835
expect(results).toHaveNoViolations();
3936
});
4037

38+
it('When no title is used, an aria-dialog-name should be provided', async () => {
39+
const { container } = renderProvider(
40+
<ActionBottomSheet
41+
closeIcon={{ icon: 'UNICORN', ['aria-label']: 'ariaLabelButton' }}
42+
open={true}
43+
popover={{ ['aria-label']: 'ariaLabel' }}
44+
variant={'DEFAULT'}
45+
>
46+
Hello
47+
</ActionBottomSheet>
48+
);
49+
50+
const popover = screen.getByText('Hello');
51+
expect(popover).toBeInTheDocument();
52+
53+
const results = await axe(container);
54+
expect(container).toHTMLValidate({
55+
rules: {
56+
'no-inline-style': 'off',
57+
},
58+
});
59+
expect(results).toHaveNoViolations();
60+
});
61+
4162
it('Should simulate icon onClick', async () => {
4263
const { container } = renderProvider(
4364
<ActionBottomSheet
@@ -68,12 +89,15 @@ describe('ActionBottomSheet component', () => {
6889
title={{ content: 'title' }}
6990
variant={'DEFAULT'}
7091
>
71-
Hello
92+
Internal content
7293
</ActionBottomSheet>
7394
);
7495
const triggerIcon = screen.getByLabelText('ariaLabelButton');
96+
97+
const internalContent = screen.getByText('Internal content');
7598
await act(async () => {
76-
fireEvent.keyDown(window, {
99+
// Internal popover element fire the escape keydown
100+
fireEvent.keyDown(internalContent, {
77101
key: 'Escape',
78102
code: 'Escape',
79103
});

src/components/actionBottomSheet/actionBottomSheetControlled.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import * as React from 'react';
22

3-
import { PopoverControlled as Popover } from '@/components/popover';
3+
import { PopoverControlled as Popover, PopoverComponentType } from '@/components/popover';
44
import { STYLES_NAME } from '@/constants';
55
import { useDeviceHeight, useId, useMediaDevice, useScrollEffect } from '@/hooks';
66
import { useStyles } from '@/hooks/useStyles/useStyles';
77
import { ErrorBoundary, FallbackComponent } from '@/provider/errorBoundary';
8-
import { DeviceBreakpointsType } from '@/types';
8+
import { DeviceBreakpointsType, ROLES } from '@/types';
99

1010
import { ActionBottomSheetStandAlone } from './actionBottomSheetStandAlone';
1111
import {
@@ -85,7 +85,7 @@ const ActionBottomSheetControlledComponent = React.forwardRef(
8585
ref: React.ForwardedRef<HTMLDivElement> | undefined | null
8686
): JSX.Element => {
8787
const uniqueTitleId = useId('actionSheet-title');
88-
const titleId = title?.id ?? uniqueTitleId;
88+
const titleId = (title?.id ?? title?.content) ? uniqueTitleId : undefined;
8989
const styles = useStyles<ActionBottomSheetVariantStylesType>(
9090
STYLES_NAME.ACTION_BOTTOM_SHEET,
9191
props.variant,
@@ -95,10 +95,13 @@ const ActionBottomSheetControlledComponent = React.forwardRef(
9595
return (
9696
<Popover
9797
aria-labelledby={titleId}
98+
aria-modal={true}
9899
clickOverlayClose={!blocked}
100+
component={PopoverComponentType.DIV}
99101
dataTestId={`${props.dataTestId}Popover`}
100102
hasBackDrop={true}
101103
open={open}
104+
role={ROLES.DIALOG}
102105
trapFocusInsideModal={true}
103106
variant={styles.popoverVariant}
104107
{...popover}

src/components/badge/__tests__/badge.test.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ import * as React from 'react';
44
import { axe } from 'jest-axe';
55

66
import { renderProvider } from '@/tests/renderProvider/renderProvider.utility';
7-
import { windowMatchMedia } from '@/tests/windowMatchMedia';
87

98
import { BadgeUnControlled as Badge } from '../badgeUnControlled';
109
import { IBadgeUnControlled } from '../types';
1110

12-
window.matchMedia = windowMatchMedia();
13-
1411
// Mocks
1512
const mockProps: IBadgeUnControlled = {
1613
dataTestId: 'badge-component',
@@ -129,7 +126,8 @@ describe('Badge component', () => {
129126
expect(contentExpand).toBeInTheDocument();
130127

131128
await act(async () => {
132-
fireEvent.keyDown(window, {
129+
// Internal popover element fire the escape keydown
130+
fireEvent.keyDown(contentExpand, {
133131
key: 'Escape',
134132
code: 'Escape',
135133
});

src/components/badge/badgeControlled.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const BagdeBoundary = <
5353
);
5454

5555
/**
56+
* @deprecated Try the new BadgeV2 component
57+
*
5658
* @description
5759
* Badge component is a component that shows a badge with a number or a dot.
5860
* @param {React.PropsWithChildren<IBadgeControlled<V>>} props

src/components/badge/badgeUnControlled.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ const BadgeUnControlledComponent = <
4949
};
5050

5151
/**
52+
* @deprecated Try the new BadgeV2 component
53+
*
5254
* @description
5355
* Badge component is a component that shows a badge with a number or a dot.
5456
* @param {React.PropsWithChildren<IBadgeUnControlled<V>>} props
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { fireEvent, screen } from '@testing-library/react';
2+
import * as React from 'react';
3+
4+
import { axe } from 'jest-axe';
5+
6+
import { renderProvider } from '@/tests/renderProvider/renderProvider.utility';
7+
8+
import { Badge } from '../badge';
9+
import { IBadge } from '../types';
10+
11+
// Mocks
12+
const mockProps: IBadge = {
13+
dataTestId: 'badge-component',
14+
variant: 'PRIMARY',
15+
size: 'DEFAULT',
16+
dot: {
17+
variant: 'WITH_BORDER',
18+
size: 'MEDIUM',
19+
number: 23,
20+
maxNumber: 99,
21+
},
22+
icon: { icon: 'CONTACTS' },
23+
label: { content: 'Notifications' },
24+
labelIcon: { icon: 'CHEVRON_DOWN' },
25+
ariaLiveText: 'New notification',
26+
['aria-label']: 'Open menu',
27+
};
28+
29+
describe('Badge component', () => {
30+
it('Should be displayed correctly', async () => {
31+
const ref = jest.fn();
32+
const { container } = renderProvider(<Badge ref={ref} {...mockProps} />);
33+
34+
const badge = screen.getByTestId(mockProps.dataTestId + 'Badge');
35+
expect(badge).toBeInTheDocument();
36+
37+
const results = await axe(container);
38+
expect(container).toHTMLValidate();
39+
expect(results).toHaveNoViolations();
40+
});
41+
42+
it('When it has a dot, the dot position is custompizable using customDotTranslate', () => {
43+
const ref = jest.fn();
44+
renderProvider(<Badge ref={ref} {...mockProps} customDotTranslate="translate(2px, 2px)" />);
45+
46+
const badge = screen.getByTestId(mockProps.dataTestId + 'Badge');
47+
expect(badge).toBeInTheDocument();
48+
});
49+
50+
it('CustomDotTranslate can also be defined via styles', () => {
51+
const ref = jest.fn();
52+
renderProvider(
53+
<Badge ref={ref} {...mockProps} cts={{ customDotNumberTranslate: '"translate(2px, 2px)' }} />
54+
);
55+
56+
const badge = screen.getByTestId(mockProps.dataTestId + 'Badge');
57+
expect(badge).toBeInTheDocument();
58+
});
59+
60+
it('Should be displayed correctly without label', async () => {
61+
const { container } = renderProvider(<Badge {...mockProps} label={undefined} />);
62+
63+
const badge = screen.getByTestId(mockProps.dataTestId + 'Badge');
64+
expect(badge).toBeInTheDocument();
65+
66+
const results = await axe(container);
67+
expect(container).toHTMLValidate();
68+
expect(results).toHaveNoViolations();
69+
});
70+
71+
it('Should be displayed correctly with simulate onClick', () => {
72+
renderProvider(<Badge {...mockProps} label={undefined} />);
73+
74+
const triggerButton = screen.getByLabelText('Open menu');
75+
fireEvent.click(triggerButton);
76+
77+
const badge = screen.getByTestId(mockProps.dataTestId + 'Badge');
78+
expect(badge).toBeInTheDocument();
79+
});
80+
81+
it('Should call onClick if defined when open', async () => {
82+
const onClick = jest.fn();
83+
const { container, getByLabelText } = renderProvider(
84+
<Badge {...mockProps} onClick={onClick} />
85+
);
86+
87+
const triggerButton = getByLabelText('Open menu');
88+
fireEvent.click(triggerButton);
89+
90+
expect(onClick).toHaveBeenCalled();
91+
92+
const results = await axe(container);
93+
expect(container).toHTMLValidate();
94+
expect(results).toHaveNoViolations();
95+
});
96+
97+
it('Should be displayed correctly when simulate onBlur', () => {
98+
renderProvider(<Badge {...mockProps} />);
99+
100+
const triggerButton = screen.getByLabelText('Open menu');
101+
fireEvent.blur(triggerButton);
102+
103+
const badge = screen.getByTestId(mockProps.dataTestId + 'Badge');
104+
expect(badge).toBeInTheDocument();
105+
});
106+
});

0 commit comments

Comments
 (0)