Skip to content

Commit ea857b9

Browse files
author
Kubit
committed
Add VirtualKeyBoard
1 parent 7305e0d commit ea857b9

File tree

23 files changed

+898
-9
lines changed

23 files changed

+898
-9
lines changed

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export * from './tableCaption';
8181
export * from './tableDivider';
8282
export * from './tableV2';
8383
export * from './dataTable';
84+
export * from './virtualKeyboard';
8485

8586
/**
8687
* Assets components.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
import { ROLES } from '@/types';
8+
9+
import { VirtualKeyboard } from '../index';
10+
import { IVirtualKeyboard } from '../types';
11+
12+
const mockProps: IVirtualKeyboard = {
13+
variant: 'DEFAULT',
14+
digits: ['0', '4', '2', '8', '7', '3', '9', '1', '6', '5'],
15+
icon: { icon: 'CLOSE', altText: 'Remove' },
16+
dataTestId: 'test-data',
17+
onDigitButtonClick: jest.fn(),
18+
onRemoveButtonClick: jest.fn(),
19+
};
20+
21+
describe('Virtual Keyboard component', () => {
22+
it('Should render component', async () => {
23+
const { container } = renderProvider(<VirtualKeyboard {...mockProps} />);
24+
25+
const buttons = screen.getAllByRole(ROLES.BUTTON);
26+
expect(buttons).toHaveLength(11);
27+
28+
const results = await axe(container);
29+
expect(container).toHTMLValidate();
30+
expect(results).toHaveNoViolations();
31+
});
32+
33+
it('Should callback onDigitButtonClick and onRemoveButtonClick when pressed', () => {
34+
const onDigitButtonClick = jest.fn();
35+
const onRemoveButtonClick = jest.fn();
36+
renderProvider(
37+
<VirtualKeyboard
38+
{...mockProps}
39+
onDigitButtonClick={onDigitButtonClick}
40+
onRemoveButtonClick={onRemoveButtonClick}
41+
/>
42+
);
43+
44+
const buttons = screen.getAllByRole(ROLES.BUTTON);
45+
fireEvent.click(buttons[0]);
46+
expect(onDigitButtonClick).toHaveBeenCalled();
47+
48+
fireEvent.click(buttons[10]);
49+
expect(onRemoveButtonClick).toHaveBeenCalled();
50+
});
51+
52+
it('When onFocus and onBlur will change internal styles, but no action will be called', () => {
53+
const onDigitButtonClick = jest.fn();
54+
const onRemoveButtonClick = jest.fn();
55+
renderProvider(
56+
<VirtualKeyboard
57+
{...mockProps}
58+
onDigitButtonClick={onDigitButtonClick}
59+
onRemoveButtonClick={onRemoveButtonClick}
60+
/>
61+
);
62+
63+
const buttons = screen.getAllByRole(ROLES.BUTTON);
64+
fireEvent.focus(buttons[0]);
65+
fireEvent.blur(buttons[0]);
66+
expect(mockProps.onDigitButtonClick).not.toHaveBeenCalled();
67+
});
68+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as React from 'react';
2+
3+
import { ButtonType } from '@/components/button';
4+
import { Text } from '@/components/text/text';
5+
import { TextComponentType } from '@/components/text/types/component';
6+
7+
import { ButtonKeyboardStateType, IDigitButton } from '../types';
8+
// styles
9+
import { DigitButtonStyled } from '../virtualKeyboard.styled';
10+
11+
/**
12+
* @description
13+
* Digit button component to be used in the virtual keyboard.
14+
* @internal
15+
*/
16+
export const DigitButton = (props: IDigitButton): JSX.Element => {
17+
const onClick: React.MouseEventHandler<HTMLButtonElement> = event => {
18+
props.onClick(props.digit, event);
19+
};
20+
return (
21+
<DigitButtonStyled
22+
data-testid={props.dataTestId}
23+
styles={props.styles}
24+
type={ButtonType.BUTTON}
25+
onClick={onClick}
26+
>
27+
<Text
28+
component={TextComponentType.SPAN}
29+
customTypography={props.styles?.digitButtons?.[ButtonKeyboardStateType.DEFAULT]?.text}
30+
>
31+
{props.digit}
32+
</Text>
33+
</DigitButtonStyled>
34+
);
35+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './types';
2+
3+
export { VirtualKeyboard } from './virtualKeyboard';
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { ICONS } from '@/assets';
2+
import { CATEGORY_CONTROL } from '@/constants/categoryControl';
3+
import { IThemeObjectVariants } from '@/designSystem/themesObject';
4+
import { objectFlip } from '@/storybook/utils/utils';
5+
import { ArgTypesReturn } from '@/types';
6+
7+
export const argtypes = (
8+
variantsObject: IThemeObjectVariants,
9+
themeSelected: string
10+
): ArgTypesReturn => {
11+
return {
12+
variant: {
13+
description: 'VirtualKeyboard variant',
14+
type: { name: 'string', required: true },
15+
control: { type: 'select' },
16+
options: Object.keys(variantsObject[themeSelected].VirtualKeyboardVariantType || {}),
17+
table: {
18+
type: {
19+
summary: 'string',
20+
},
21+
category: CATEGORY_CONTROL.MODIFIERS,
22+
},
23+
},
24+
digits: {
25+
description: 'There will be range number buttons from 0 value to 9 value placed randomly.',
26+
type: { name: 'array', required: true },
27+
control: { type: 'object' },
28+
table: {
29+
type: {
30+
summary: 'string[]',
31+
},
32+
category: CATEGORY_CONTROL.MODIFIERS,
33+
},
34+
},
35+
icon: {
36+
description: 'Icon of the remove button',
37+
type: { name: 'string' },
38+
control: { type: 'select', labels: objectFlip(ICONS) },
39+
options: Object.values(ICONS),
40+
table: {
41+
type: {
42+
summary: 'string',
43+
},
44+
category: CATEGORY_CONTROL.CONTENT,
45+
},
46+
},
47+
iconAltText: {
48+
description: 'Alt text of the icon',
49+
type: { name: 'string' },
50+
control: { type: 'text' },
51+
table: {
52+
type: {
53+
summary: 'string',
54+
},
55+
category: CATEGORY_CONTROL.ACCESIBILITY,
56+
},
57+
},
58+
onDigitButtonClick: {
59+
description: 'The event occurs when the user clicks on the digit button',
60+
type: { name: 'function', required: true },
61+
control: false,
62+
table: {
63+
type: {
64+
summary:
65+
'(digit: string, event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void',
66+
},
67+
category: CATEGORY_CONTROL.FUNCTIONS,
68+
},
69+
},
70+
onRemoveButtonClick: {
71+
description:
72+
'User can remove what they have clicked. The event occurs when the user clicks on the remove button',
73+
type: { name: 'function', required: true },
74+
control: false,
75+
table: {
76+
type: {
77+
summary: 'React.MouseEventHandler<HTMLButtonElement>',
78+
},
79+
category: CATEGORY_CONTROL.FUNCTIONS,
80+
},
81+
},
82+
id: {
83+
description: 'Identifier of the virtual keyboard',
84+
type: { name: 'string' },
85+
control: { type: 'text' },
86+
table: {
87+
type: {
88+
summary: 'string',
89+
},
90+
category: CATEGORY_CONTROL.ACCESIBILITY,
91+
},
92+
},
93+
dataTestId: {
94+
description:
95+
'Test id of the virtual keyboard. Internal components will concatenate from this test id',
96+
type: { name: 'string' },
97+
control: { type: 'text' },
98+
table: {
99+
type: {
100+
summary: 'string',
101+
},
102+
defaultValue: {
103+
summary: 'virtualkeyboard',
104+
},
105+
category: CATEGORY_CONTROL.TESTING,
106+
},
107+
},
108+
ctv: {
109+
description: 'Object used for update variant styles',
110+
type: { name: 'object' },
111+
control: { type: 'object' },
112+
table: {
113+
type: {
114+
summary: 'object',
115+
},
116+
category: CATEGORY_CONTROL.CUSTOMIZATION,
117+
},
118+
},
119+
};
120+
};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type { Meta, StoryObj } from '@storybook/react';
2+
3+
import { ICONS } from '@/assets';
4+
import { STYLES_NAME } from '@/constants';
5+
import { themesObject, variantsObject } from '@/designSystem/themesObject';
6+
7+
import { IVirtualKeyboard } from '../types';
8+
import { VirtualKeyboard as Story } from '../virtualKeyboard';
9+
import { argtypes } from './argtypes';
10+
11+
const themeSelected = localStorage.getItem('themeSelected') || 'kubit';
12+
13+
const meta = {
14+
title: 'Components/Forms/VirtualKeyboard',
15+
component: Story,
16+
tags: ['autodocs'],
17+
argTypes: argtypes(variantsObject, themeSelected),
18+
} satisfies Meta<typeof Story>;
19+
20+
export default meta;
21+
22+
type Story = StoryObj<typeof meta> & { args: { themeArgs?: object } };
23+
24+
const commonArgs: IVirtualKeyboard = {
25+
variant: Object.values(
26+
variantsObject[themeSelected].VirtualKeyboardVariantType || {}
27+
)[0] as string,
28+
digits: ['0', '4', '2', '8', '7', '3', '9', '1', '6', '5'],
29+
icon: { icon: ICONS.ICON_PLACEHOLDER },
30+
onDigitButtonClick: () => {
31+
return null;
32+
},
33+
onRemoveButtonClick: () => {
34+
return null;
35+
},
36+
};
37+
38+
export const VirtualKeyboard: Story = {
39+
args: {
40+
...commonArgs,
41+
themeArgs: themesObject[themeSelected][STYLES_NAME.VIRTUAL_KEYBOARD],
42+
},
43+
};
44+
45+
export const VirtualKeyboardWithCtv: Story = {
46+
args: {
47+
...commonArgs,
48+
ctv: {
49+
INACTIVE: {
50+
digitButtons: {
51+
wrapper: {
52+
background_color: 'pink',
53+
},
54+
},
55+
},
56+
},
57+
},
58+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './virtualKeyboard';
2+
export * from './virtualKeyboardTheme';
3+
export * from './state';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @name VirtualKeyboardStateType
3+
* @description
4+
* Enum for the state of the virtual keyboard
5+
*/
6+
export enum VirtualKeyboardStateType {
7+
INACTIVE = 'INACTIVE',
8+
ACTIVE = 'ACTIVE',
9+
}
10+
11+
/**
12+
* @name ButtonKeyboardStateType
13+
* @description
14+
* Enum for the state of the button keyboard
15+
*/
16+
export enum ButtonKeyboardStateType {
17+
DEFAULT = 'DEFAULT',
18+
PRESSED = 'PRESSED',
19+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { IElementOrIcon } from '@/components/elementOrIcon';
2+
import { IText } from '@/components/text';
3+
import { CustomTokenTypes } from '@/types';
4+
5+
import { VirtualKeyboardStateType } from './state';
6+
import {
7+
VirtualKeyboardPropsStylesType,
8+
VirtualKeyboardStateStylesType,
9+
} from './virtualKeyboardTheme';
10+
11+
export type VirtualKeyboardTextType = Omit<IText<string>, 'children'> & {
12+
content: string;
13+
};
14+
15+
/**
16+
* @name IVirtualKeyboard
17+
* @description
18+
* Interface for the VirtualKeyboard component
19+
*/
20+
export interface IDigitButton {
21+
styles?: VirtualKeyboardPropsStylesType;
22+
id?: string;
23+
// modifiers
24+
digit: string;
25+
// functions
26+
onClick: (digit: string, event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
27+
// tests
28+
dataTestId?: string;
29+
}
30+
31+
/**
32+
* @name IVirtualKeyboard
33+
* @description
34+
* Interface for the VirtualKeyboard component
35+
*/
36+
export interface IVirtualKeyboardStandAlone {
37+
styles: VirtualKeyboardStateStylesType;
38+
id?: string;
39+
// modifiers
40+
state: VirtualKeyboardStateType;
41+
digits: string[];
42+
// functions
43+
onVirtualKeyboardFocus: React.FocusEventHandler<HTMLDivElement>;
44+
onVirtualKeyboardBlur: React.FocusEventHandler<HTMLDivElement>;
45+
onDigitButtonClick: (
46+
digit: string,
47+
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
48+
) => void;
49+
onRemoveButtonClick: React.MouseEventHandler<HTMLButtonElement>;
50+
// icons
51+
icon: IElementOrIcon;
52+
// tests
53+
dataTestId?: string;
54+
}
55+
56+
type propsToOmit = 'styles' | 'state' | 'onVirtualKeyboardFocus' | 'onVirtualKeyboardBlur';
57+
58+
/**
59+
* @name IVirtualKeyboard
60+
* @description
61+
* Interface for the VirtualKeyboard component
62+
*/
63+
export interface IVirtualKeyboard<V = undefined extends string ? unknown : string>
64+
extends Omit<IVirtualKeyboardStandAlone, propsToOmit>,
65+
Omit<CustomTokenTypes<VirtualKeyboardStateStylesType>, 'cts' | 'extraCt'> {
66+
variant: V;
67+
}

0 commit comments

Comments
 (0)