@ippon-ui/ui 0.0.2
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/.agents/agents/component-library-create-from-pattern-library.agent.md +37 -0
- package/.agents/agents/patten-library-create-component.agent.md +30 -0
- package/.agents/skills/component-library/SKILL.md +169 -0
- package/.agents/skills/pattern-library/SKILL.md +277 -0
- package/.github/workflows/build.yml +34 -0
- package/.gitlab-ci.yml +12 -0
- package/.prettierignore +6 -0
- package/AGENTS.md +50 -0
- package/LICENSE +202 -0
- package/ci/build.yml +15 -0
- package/ci/common.yml +42 -0
- package/ci/deploy.yml +20 -0
- package/icons/LICENCE +202 -0
- package/icons/index.ts +69 -0
- package/icons/package.json +25 -0
- package/icons/tsconfig.json +11 -0
- package/lefthook.yml +10 -0
- package/mise.toml +55 -0
- package/package.json +26 -0
- package/pnpm-workspace.yaml +9 -0
- package/prettier.config.mts +8 -0
- package/react/LICENCE +202 -0
- package/react/README.md +75 -0
- package/react/eslint.config.js +22 -0
- package/react/package.json +63 -0
- package/react/src/CAP.ts +14 -0
- package/react/src/Card.ts +2 -0
- package/react/src/DataSelectable.ts +7 -0
- package/react/src/Grid.ts +33 -0
- package/react/src/IpponBadge.tsx +62 -0
- package/react/src/IpponButton.tsx +93 -0
- package/react/src/IpponButtonCard.tsx +34 -0
- package/react/src/IpponCard.tsx +30 -0
- package/react/src/IpponContainer.tsx +15 -0
- package/react/src/IpponGrid.tsx +56 -0
- package/react/src/IpponHSpace.tsx +56 -0
- package/react/src/IpponIcon.tsx +15 -0
- package/react/src/IpponImportFile.tsx +128 -0
- package/react/src/IpponIon.tsx +45 -0
- package/react/src/IpponMeter.tsx +43 -0
- package/react/src/IpponProgress.tsx +45 -0
- package/react/src/IpponText.tsx +56 -0
- package/react/src/IpponTitle.tsx +45 -0
- package/react/src/IpponVSpace.tsx +43 -0
- package/react/src/Optional.ts +177 -0
- package/react/src/Tokens.ts +36 -0
- package/react/src/index.ts +16 -0
- package/react/test/File.fixture.ts +13 -0
- package/react/test/IpponBadge.spec.tsx +245 -0
- package/react/test/IpponButton.spec.tsx +666 -0
- package/react/test/IpponButtonCard.spec.tsx +162 -0
- package/react/test/IpponCard.spec.tsx +133 -0
- package/react/test/IpponContainer.spec.tsx +56 -0
- package/react/test/IpponGrid.spec.tsx +140 -0
- package/react/test/IpponHSpace.spec.tsx +107 -0
- package/react/test/IpponIcon.spec.tsx +37 -0
- package/react/test/IpponImportFile.spec.tsx +431 -0
- package/react/test/IpponIon.spec.tsx +52 -0
- package/react/test/IpponMeter.spec.tsx +59 -0
- package/react/test/IpponProgress.spec.tsx +68 -0
- package/react/test/IpponText.spec.tsx +149 -0
- package/react/test/IpponTitle.spec.tsx +242 -0
- package/react/test/IpponVSpace.spec.tsx +91 -0
- package/react/tsconfig.app.json +24 -0
- package/react/tsconfig.json +4 -0
- package/react/tsconfig.node.json +23 -0
- package/react/vite.config.ts +30 -0
- package/react/vitest.config.ts +21 -0
- package/styles/.editorconfig +12 -0
- package/styles/.stylelintrc.json +75 -0
- package/styles/LICENCE +202 -0
- package/styles/README.md +107 -0
- package/styles/logo.svg +26 -0
- package/styles/package.json +67 -0
- package/styles/src/atom/_atom.scss +9 -0
- package/styles/src/atom/atom.pug +34 -0
- package/styles/src/atom/badge/_badge.scss +108 -0
- package/styles/src/atom/badge/badge.code.pug +29 -0
- package/styles/src/atom/badge/badge.md +1 -0
- package/styles/src/atom/badge/badge.mixin.pug +24 -0
- package/styles/src/atom/badge/badge.render.pug +7 -0
- package/styles/src/atom/button/_button.scss +242 -0
- package/styles/src/atom/button/button.code.pug +38 -0
- package/styles/src/atom/button/button.md +31 -0
- package/styles/src/atom/button/button.mixin.pug +30 -0
- package/styles/src/atom/button/button.render.pug +13 -0
- package/styles/src/atom/icon/_icon.scss +8 -0
- package/styles/src/atom/icon/icon.code.pug +5 -0
- package/styles/src/atom/icon/icon.md +11 -0
- package/styles/src/atom/icon/icon.mixin.pug +8 -0
- package/styles/src/atom/icon/icon.render.pug +7 -0
- package/styles/src/atom/ion/ion.code.pug +8 -0
- package/styles/src/atom/ion/ion.md +11 -0
- package/styles/src/atom/ion/ion.mixin.pug +8 -0
- package/styles/src/atom/ion/ion.render.pug +7 -0
- package/styles/src/atom/meter/_meter.scss +23 -0
- package/styles/src/atom/meter/meter.code.pug +8 -0
- package/styles/src/atom/meter/meter.md +7 -0
- package/styles/src/atom/meter/meter.mixin.pug +12 -0
- package/styles/src/atom/meter/meter.render.pug +5 -0
- package/styles/src/atom/progress/_progress.scss +23 -0
- package/styles/src/atom/progress/progress.code.pug +8 -0
- package/styles/src/atom/progress/progress.md +7 -0
- package/styles/src/atom/progress/progress.mixin.pug +14 -0
- package/styles/src/atom/progress/progress.render.pug +5 -0
- package/styles/src/atom/tab/_tab.scss +48 -0
- package/styles/src/atom/tab/tab.code.pug +5 -0
- package/styles/src/atom/tab/tab.md +1 -0
- package/styles/src/atom/tab/tab.mixin.pug +14 -0
- package/styles/src/atom/tab/tab.render.pug +4 -0
- package/styles/src/atom/text/_text.scss +74 -0
- package/styles/src/atom/text/text.code.pug +19 -0
- package/styles/src/atom/text/text.md +5 -0
- package/styles/src/atom/text/text.mixin.pug +12 -0
- package/styles/src/atom/text/text.render.pug +7 -0
- package/styles/src/atom/title/_title.scss +68 -0
- package/styles/src/atom/title/title.code.pug +25 -0
- package/styles/src/atom/title/title.md +9 -0
- package/styles/src/atom/title/title.mixin.pug +12 -0
- package/styles/src/atom/title/title.render.pug +5 -0
- package/styles/src/atom/title-display/_title-display.scss +26 -0
- package/styles/src/atom/title-display/title-display.code.pug +9 -0
- package/styles/src/atom/title-display/title-display.md +5 -0
- package/styles/src/atom/title-display/title-display.mixin.pug +6 -0
- package/styles/src/atom/title-display/title-display.render.pug +4 -0
- package/styles/src/doc.scss +2 -0
- package/styles/src/favicon.ico +0 -0
- package/styles/src/function/_conversion.scss +9 -0
- package/styles/src/index.pug +59 -0
- package/styles/src/layout-documentation.pug +14 -0
- package/styles/src/layout.pug +17 -0
- package/styles/src/molecule/_molecule.scss +2 -0
- package/styles/src/molecule/import-file/_import-file.scss +38 -0
- package/styles/src/molecule/import-file/import-file.code.pug +4 -0
- package/styles/src/molecule/import-file/import-file.md +1 -0
- package/styles/src/molecule/import-file/import-file.mixin.pug +15 -0
- package/styles/src/molecule/import-file/import-file.render.pug +5 -0
- package/styles/src/molecule/molecule.pug +20 -0
- package/styles/src/molecule/tabs/_tabs.scss +4 -0
- package/styles/src/molecule/tabs/tabs.code.pug +9 -0
- package/styles/src/molecule/tabs/tabs.md +1 -0
- package/styles/src/molecule/tabs/tabs.mixin.pug +4 -0
- package/styles/src/molecule/tabs/tabs.render.pug +4 -0
- package/styles/src/molecule/toggle/_toggle.scss +68 -0
- package/styles/src/molecule/toggle/toggle.code.pug +26 -0
- package/styles/src/molecule/toggle/toggle.md +1 -0
- package/styles/src/molecule/toggle/toggle.mixin.pug +36 -0
- package/styles/src/molecule/toggle/toggle.render.pug +5 -0
- package/styles/src/organism/_abstract-card.scss +36 -0
- package/styles/src/organism/_docorganism.scss +1 -0
- package/styles/src/organism/_organism.scss +8 -0
- package/styles/src/organism/button-card/_button-card.scss +22 -0
- package/styles/src/organism/button-card/button-card.code.pug +31 -0
- package/styles/src/organism/button-card/button-card.md +28 -0
- package/styles/src/organism/button-card/button-card.mixin.pug +8 -0
- package/styles/src/organism/button-card/button-card.render.pug +7 -0
- package/styles/src/organism/card/_card.scss +6 -0
- package/styles/src/organism/card/card.code.pug +9 -0
- package/styles/src/organism/card/card.md +23 -0
- package/styles/src/organism/card/card.mixin.pug +7 -0
- package/styles/src/organism/card/card.render.pug +7 -0
- package/styles/src/organism/container/_container.scss +3 -0
- package/styles/src/organism/container/container.code.pug +13 -0
- package/styles/src/organism/container/container.md +5 -0
- package/styles/src/organism/container/container.mixin.pug +3 -0
- package/styles/src/organism/container/container.render.pug +4 -0
- package/styles/src/organism/grid/_docgrid.scss +11 -0
- package/styles/src/organism/grid/_grid.scss +84 -0
- package/styles/src/organism/grid/grid.code.pug +25 -0
- package/styles/src/organism/grid/grid.md +1 -0
- package/styles/src/organism/grid/grid.mixin.pug +7 -0
- package/styles/src/organism/grid/grid.render.pug +5 -0
- package/styles/src/organism/h-space/_h-space.scss +49 -0
- package/styles/src/organism/h-space/h-space.code.pug +56 -0
- package/styles/src/organism/h-space/h-space.md +22 -0
- package/styles/src/organism/h-space/h-space.mixin.pug +14 -0
- package/styles/src/organism/h-space/h-space.render.pug +5 -0
- package/styles/src/organism/header/_header.scss +8 -0
- package/styles/src/organism/header/header.code.pug +14 -0
- package/styles/src/organism/header/header.md +1 -0
- package/styles/src/organism/header/header.mixin.pug +7 -0
- package/styles/src/organism/header/header.render.pug +4 -0
- package/styles/src/organism/modal/_modal.scss +58 -0
- package/styles/src/organism/modal/modal.code.pug +68 -0
- package/styles/src/organism/modal/modal.md +1 -0
- package/styles/src/organism/modal/modal.mixin.pug +25 -0
- package/styles/src/organism/modal/modal.render.pug +4 -0
- package/styles/src/organism/organism.pug +30 -0
- package/styles/src/organism/v-space/_v-space.scss +45 -0
- package/styles/src/organism/v-space/v-space.code.pug +41 -0
- package/styles/src/organism/v-space/v-space.md +20 -0
- package/styles/src/organism/v-space/v-space.mixin.pug +7 -0
- package/styles/src/organism/v-space/v-space.render.pug +5 -0
- package/styles/src/quark/_breakpoint.scss +12 -0
- package/styles/src/quark/_font.scss +38 -0
- package/styles/src/quark/_gap.scss +34 -0
- package/styles/src/quark/_placeholder.scss +27 -0
- package/styles/src/quark/_shadow.scss +13 -0
- package/styles/src/quark/_typography.scss +146 -0
- package/styles/src/template/_template.scss +1 -0
- package/styles/src/template/layout/_layout.scss +20 -0
- package/styles/src/template/layout/layout.code.pug +11 -0
- package/styles/src/template/layout/layout.md +1 -0
- package/styles/src/template/layout/layout.mixin.pug +11 -0
- package/styles/src/template/layout/layout.render.pug +4 -0
- package/styles/src/template/template.pug +16 -0
- package/styles/src/tikui.scss +5 -0
- package/styles/src/token/_doctable.scss +14 -0
- package/styles/src/token/_doctoken.scss +1 -0
- package/styles/src/token/_size.scss +9 -0
- package/styles/src/token/_token.scss +5 -0
- package/styles/src/token/color/_color.scss +9 -0
- package/styles/src/token/color/color/_base.scss +65 -0
- package/styles/src/token/color/color/_brand.scss +13 -0
- package/styles/src/token/color/color/_error.scss +13 -0
- package/styles/src/token/color/color/_information-2.scss +13 -0
- package/styles/src/token/color/color/_information.scss +13 -0
- package/styles/src/token/color/color/_neutral.scss +20 -0
- package/styles/src/token/color/color/_semantic.scss +69 -0
- package/styles/src/token/color/color/_success.scss +13 -0
- package/styles/src/token/color/color/_warning.scss +13 -0
- package/styles/src/token/color/color.js +31 -0
- package/styles/src/token/color/color.mixin.pug +19 -0
- package/styles/src/token/color/color.pug +9 -0
- package/styles/src/token/color/color.render.pug +13 -0
- package/styles/src/token/radius/_radius.scss +8 -0
- package/styles/src/token/radius/radius.js +54 -0
- package/styles/src/token/radius/radius.mixin.pug +14 -0
- package/styles/src/token/radius/radius.pug +9 -0
- package/styles/src/token/radius/radius.render.pug +11 -0
- package/styles/src/token/shadow/_shadow.scss +22 -0
- package/styles/src/token/shadow/shadow.js +45 -0
- package/styles/src/token/shadow/shadow.mixin.pug +13 -0
- package/styles/src/token/shadow/shadow.pug +9 -0
- package/styles/src/token/shadow/shadow.render.pug +9 -0
- package/styles/src/token/token.js +38 -0
- package/styles/src/token/token.pug +25 -0
- package/styles/src/token/typography/_typography.scss +103 -0
- package/styles/src/token/typography/typography.js +32 -0
- package/styles/src/token/typography/typography.mixin.pug +17 -0
- package/styles/src/token/typography/typography.pug +9 -0
- package/styles/src/token/typography/typography.render.pug +19 -0
- package/styles/test/function/conversion.test.scss +20 -0
- package/styles/test/function/sass.spec.ts +6 -0
- package/styles/tikuiconfig.json +14 -0
- package/styles/tsconfig.json +10 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { render, screen, configure, cleanup, fireEvent } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom/vitest';
|
|
4
|
+
import { IpponImportFile } from '../src';
|
|
5
|
+
import { fakeTextFile } from './File.fixture.ts';
|
|
6
|
+
|
|
7
|
+
configure({
|
|
8
|
+
testIdAttribute: 'data-selector',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const getIpponImportFile = () => screen.getByTestId('ippon-import-file');
|
|
12
|
+
|
|
13
|
+
const expectToHaveClasses = (...classes: string[]) =>
|
|
14
|
+
expect(getIpponImportFile()).toHaveClass('ippon-import-file', ...classes);
|
|
15
|
+
|
|
16
|
+
const getFileInput = () =>
|
|
17
|
+
getIpponImportFile().querySelector('input[type="file"]') as HTMLInputElement;
|
|
18
|
+
|
|
19
|
+
describe('IpponImportFile', () => {
|
|
20
|
+
beforeEach(cleanup);
|
|
21
|
+
|
|
22
|
+
it('should be minimal', () => {
|
|
23
|
+
render(
|
|
24
|
+
<IpponImportFile
|
|
25
|
+
title="Upload files"
|
|
26
|
+
description="Click to browse"
|
|
27
|
+
dataSelector="ippon-import-file"
|
|
28
|
+
/>,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
expectToHaveClasses();
|
|
32
|
+
expect(getFileInput()).toBeInTheDocument();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('dragover state', () => {
|
|
36
|
+
it('should have dragover class when dragenter event fires', () => {
|
|
37
|
+
render(
|
|
38
|
+
<IpponImportFile
|
|
39
|
+
title="Upload files"
|
|
40
|
+
description="Click to browse"
|
|
41
|
+
dataSelector="ippon-import-file"
|
|
42
|
+
/>,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
fireEvent.dragEnter(getIpponImportFile());
|
|
46
|
+
|
|
47
|
+
expectToHaveClasses('-dragover');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should not have dragover class when dragleave event fires and counter reaches 0', () => {
|
|
51
|
+
render(
|
|
52
|
+
<IpponImportFile
|
|
53
|
+
title="Upload files"
|
|
54
|
+
description="Click to browse"
|
|
55
|
+
dataSelector="ippon-import-file"
|
|
56
|
+
/>,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
fireEvent.dragEnter(getIpponImportFile());
|
|
60
|
+
expectToHaveClasses('-dragover');
|
|
61
|
+
|
|
62
|
+
fireEvent.dragLeave(getIpponImportFile());
|
|
63
|
+
expect(getIpponImportFile()).not.toHaveClass('-dragover');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should not have dragover class after drop event', () => {
|
|
67
|
+
render(
|
|
68
|
+
<IpponImportFile
|
|
69
|
+
title="Upload files"
|
|
70
|
+
description="Click to browse"
|
|
71
|
+
dataSelector="ippon-import-file"
|
|
72
|
+
/>,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
fireEvent.dragEnter(getIpponImportFile());
|
|
76
|
+
expectToHaveClasses('-dragover');
|
|
77
|
+
|
|
78
|
+
fireEvent.drop(getIpponImportFile());
|
|
79
|
+
expect(getIpponImportFile()).not.toHaveClass('-dragover');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should maintain dragover class with multiple dragenter events', () => {
|
|
83
|
+
render(
|
|
84
|
+
<IpponImportFile
|
|
85
|
+
title="Upload files"
|
|
86
|
+
description="Click to browse"
|
|
87
|
+
dataSelector="ippon-import-file"
|
|
88
|
+
/>,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
fireEvent.dragEnter(getIpponImportFile());
|
|
92
|
+
fireEvent.dragEnter(getIpponImportFile());
|
|
93
|
+
expectToHaveClasses('-dragover');
|
|
94
|
+
|
|
95
|
+
fireEvent.dragLeave(getIpponImportFile());
|
|
96
|
+
expectToHaveClasses('-dragover');
|
|
97
|
+
|
|
98
|
+
fireEvent.dragLeave(getIpponImportFile());
|
|
99
|
+
expect(getIpponImportFile()).not.toHaveClass('-dragover');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should not throw on dragover event', () => {
|
|
103
|
+
render(
|
|
104
|
+
<IpponImportFile
|
|
105
|
+
title="Upload files"
|
|
106
|
+
description="Click to browse"
|
|
107
|
+
dataSelector="ippon-import-file"
|
|
108
|
+
/>,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(() => {
|
|
112
|
+
fireEvent.dragOver(getIpponImportFile());
|
|
113
|
+
}).not.toThrow();
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('file input', () => {
|
|
118
|
+
it('should render hidden file input', () => {
|
|
119
|
+
render(
|
|
120
|
+
<IpponImportFile
|
|
121
|
+
title="Upload files"
|
|
122
|
+
description="Click to browse"
|
|
123
|
+
dataSelector="ippon-import-file"
|
|
124
|
+
/>,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const input = getFileInput();
|
|
128
|
+
expect(input).toBeInTheDocument();
|
|
129
|
+
expect(input).toHaveAttribute('type', 'file');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should accept multiple files when multiple is true', () => {
|
|
133
|
+
render(
|
|
134
|
+
<IpponImportFile
|
|
135
|
+
multiple={true}
|
|
136
|
+
title="Upload files"
|
|
137
|
+
description="Click to browse"
|
|
138
|
+
dataSelector="ippon-import-file"
|
|
139
|
+
/>,
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
expect(getFileInput()).toHaveAttribute('multiple');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should accept specific file types when accept is provided', () => {
|
|
146
|
+
render(
|
|
147
|
+
<IpponImportFile
|
|
148
|
+
accept=".pdf,.doc"
|
|
149
|
+
title="Upload files"
|
|
150
|
+
description="Click to browse"
|
|
151
|
+
dataSelector="ippon-import-file"
|
|
152
|
+
/>,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
expect(getFileInput()).toHaveAttribute('accept', '.pdf,.doc');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should have data-selector on input with .input suffix', () => {
|
|
159
|
+
render(
|
|
160
|
+
<IpponImportFile
|
|
161
|
+
title="Upload files"
|
|
162
|
+
description="Click to browse"
|
|
163
|
+
dataSelector="ippon-import-file"
|
|
164
|
+
/>,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
expect(getFileInput()).toHaveAttribute('data-selector', 'ippon-import-file.input');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should not have data-selector on input when dataSelector is not provided', () => {
|
|
171
|
+
render(<IpponImportFile title="Upload files" description="Click to browse" />);
|
|
172
|
+
|
|
173
|
+
const input = screen
|
|
174
|
+
.getByRole('presentation')
|
|
175
|
+
.closest('label')
|
|
176
|
+
?.querySelector('input[type="file"]');
|
|
177
|
+
expect(input).not.toHaveAttribute('data-selector');
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('icon and text content', () => {
|
|
182
|
+
it('should render icon with cloud-upload name', () => {
|
|
183
|
+
render(
|
|
184
|
+
<IpponImportFile
|
|
185
|
+
title="Upload files"
|
|
186
|
+
description="Click to browse"
|
|
187
|
+
dataSelector="ippon-import-file"
|
|
188
|
+
/>,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const icon = getIpponImportFile().querySelector('.ippon-icon');
|
|
192
|
+
expect(icon).toBeInTheDocument();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should render default text', () => {
|
|
196
|
+
render(
|
|
197
|
+
<IpponImportFile
|
|
198
|
+
title="Drag and drop your files here"
|
|
199
|
+
description="Or click to browse"
|
|
200
|
+
dataSelector="ippon-import-file"
|
|
201
|
+
/>,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
expect(getIpponImportFile()).toHaveTextContent('Drag and drop your files here');
|
|
205
|
+
expect(getIpponImportFile()).toHaveTextContent('Or click to browse');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should render custom title', () => {
|
|
209
|
+
render(
|
|
210
|
+
<IpponImportFile
|
|
211
|
+
title="Custom title text"
|
|
212
|
+
description="Click to browse"
|
|
213
|
+
dataSelector="ippon-import-file"
|
|
214
|
+
/>,
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
expect(getIpponImportFile()).toHaveTextContent('Custom title text');
|
|
218
|
+
expect(getIpponImportFile()).toHaveTextContent('Click to browse');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should render custom description', () => {
|
|
222
|
+
render(
|
|
223
|
+
<IpponImportFile
|
|
224
|
+
title="Upload files"
|
|
225
|
+
description="Custom description text"
|
|
226
|
+
dataSelector="ippon-import-file"
|
|
227
|
+
/>,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
expect(getIpponImportFile()).toHaveTextContent('Upload files');
|
|
231
|
+
expect(getIpponImportFile()).toHaveTextContent('Custom description text');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should render both custom title and description', () => {
|
|
235
|
+
render(
|
|
236
|
+
<IpponImportFile
|
|
237
|
+
title="Custom title"
|
|
238
|
+
description="Custom description"
|
|
239
|
+
dataSelector="ippon-import-file"
|
|
240
|
+
/>,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect(getIpponImportFile()).toHaveTextContent('Custom title');
|
|
244
|
+
expect(getIpponImportFile()).toHaveTextContent('Custom description');
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe('onChange', () => {
|
|
249
|
+
it('should call onChange with single File in single mode', () => {
|
|
250
|
+
const onChange = vi.fn();
|
|
251
|
+
render(
|
|
252
|
+
<IpponImportFile
|
|
253
|
+
title="Upload file"
|
|
254
|
+
description="Click to browse"
|
|
255
|
+
onChange={onChange}
|
|
256
|
+
dataSelector="ippon-import-file"
|
|
257
|
+
/>,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
const input = getFileInput();
|
|
261
|
+
const file = fakeTextFile({ content: 'content', name: 'test.txt', type: 'text/plain' });
|
|
262
|
+
|
|
263
|
+
fireEvent.change(input, { target: { files: [file] } });
|
|
264
|
+
|
|
265
|
+
expect(onChange).toHaveBeenCalledOnce();
|
|
266
|
+
expect(onChange).toHaveBeenCalledWith(file);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should call onChange with File[] in multiple mode', () => {
|
|
270
|
+
const onChange = vi.fn();
|
|
271
|
+
render(
|
|
272
|
+
<IpponImportFile
|
|
273
|
+
title="Upload files"
|
|
274
|
+
description="Click to browse"
|
|
275
|
+
multiple={true}
|
|
276
|
+
onChange={onChange}
|
|
277
|
+
dataSelector="ippon-import-file"
|
|
278
|
+
/>,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
const input = getFileInput();
|
|
282
|
+
const file1 = fakeTextFile({ content: 'content1', name: 'test1.txt', type: 'text/plain' });
|
|
283
|
+
const file2 = fakeTextFile({ content: 'content2', name: 'test2.txt', type: 'text/plain' });
|
|
284
|
+
|
|
285
|
+
fireEvent.change(input, { target: { files: [file1, file2] } });
|
|
286
|
+
|
|
287
|
+
expect(onChange).toHaveBeenCalledOnce();
|
|
288
|
+
expect(onChange).toHaveBeenCalledWith([file1, file2]);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should not call onChange when files list is empty', () => {
|
|
292
|
+
const onChange = vi.fn();
|
|
293
|
+
render(
|
|
294
|
+
<IpponImportFile
|
|
295
|
+
title="Upload file"
|
|
296
|
+
description="Click to browse"
|
|
297
|
+
onChange={onChange}
|
|
298
|
+
dataSelector="ippon-import-file"
|
|
299
|
+
/>,
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
const input = getFileInput();
|
|
303
|
+
|
|
304
|
+
fireEvent.change(input, { target: { files: [] } });
|
|
305
|
+
|
|
306
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should not throw error when onChange is not provided', () => {
|
|
310
|
+
render(
|
|
311
|
+
<IpponImportFile
|
|
312
|
+
title="Upload file"
|
|
313
|
+
description="Click to browse"
|
|
314
|
+
dataSelector="ippon-import-file"
|
|
315
|
+
/>,
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
const input = getFileInput();
|
|
319
|
+
const file = fakeTextFile({ content: 'content', name: 'test.txt', type: 'text/plain' });
|
|
320
|
+
|
|
321
|
+
expect(() => {
|
|
322
|
+
fireEvent.change(input, { target: { files: [file] } });
|
|
323
|
+
}).not.toThrow();
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should call onChange with single File when a file is dropped in single mode', () => {
|
|
327
|
+
const onChange = vi.fn();
|
|
328
|
+
render(
|
|
329
|
+
<IpponImportFile
|
|
330
|
+
title="Upload file"
|
|
331
|
+
description="Click to browse"
|
|
332
|
+
onChange={onChange}
|
|
333
|
+
dataSelector="ippon-import-file"
|
|
334
|
+
/>,
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
const file = fakeTextFile({ content: 'content', name: 'test.txt', type: 'text/plain' });
|
|
338
|
+
|
|
339
|
+
fireEvent.drop(getIpponImportFile(), { dataTransfer: { files: [file] } });
|
|
340
|
+
|
|
341
|
+
expect(onChange).toHaveBeenCalledOnce();
|
|
342
|
+
expect(onChange).toHaveBeenCalledWith(file);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('should call onChange with File[] when files are dropped in multiple mode', () => {
|
|
346
|
+
const onChange = vi.fn();
|
|
347
|
+
render(
|
|
348
|
+
<IpponImportFile
|
|
349
|
+
title="Upload files"
|
|
350
|
+
description="Click to browse"
|
|
351
|
+
multiple={true}
|
|
352
|
+
onChange={onChange}
|
|
353
|
+
dataSelector="ippon-import-file"
|
|
354
|
+
/>,
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
const file1 = fakeTextFile({ content: 'content1', name: 'test1.txt', type: 'text/plain' });
|
|
358
|
+
const file2 = fakeTextFile({ content: 'content2', name: 'test2.txt', type: 'text/plain' });
|
|
359
|
+
|
|
360
|
+
fireEvent.drop(getIpponImportFile(), { dataTransfer: { files: [file1, file2] } });
|
|
361
|
+
|
|
362
|
+
expect(onChange).toHaveBeenCalledOnce();
|
|
363
|
+
expect(onChange).toHaveBeenCalledWith([file1, file2]);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('should not call onChange when no files are dropped', () => {
|
|
367
|
+
const onChange = vi.fn();
|
|
368
|
+
render(
|
|
369
|
+
<IpponImportFile
|
|
370
|
+
title="Upload file"
|
|
371
|
+
description="Click to browse"
|
|
372
|
+
onChange={onChange}
|
|
373
|
+
dataSelector="ippon-import-file"
|
|
374
|
+
/>,
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
fireEvent.drop(getIpponImportFile(), { dataTransfer: { files: [] } });
|
|
378
|
+
|
|
379
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('should call onChange twice when selecting the same file consecutively via input', () => {
|
|
383
|
+
const onChange = vi.fn();
|
|
384
|
+
render(
|
|
385
|
+
<IpponImportFile
|
|
386
|
+
title="Upload file"
|
|
387
|
+
description="Click to browse"
|
|
388
|
+
onChange={onChange}
|
|
389
|
+
dataSelector="ippon-import-file"
|
|
390
|
+
/>,
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
const input = getFileInput();
|
|
394
|
+
const file = fakeTextFile({ content: 'content', name: 'test.txt', type: 'text/plain' });
|
|
395
|
+
|
|
396
|
+
// First selection
|
|
397
|
+
fireEvent.change(input, { target: { files: [file] } });
|
|
398
|
+
expect(onChange).toHaveBeenCalledOnce();
|
|
399
|
+
expect(onChange).toHaveBeenCalledWith(file);
|
|
400
|
+
|
|
401
|
+
// Second selection of the same file
|
|
402
|
+
fireEvent.change(input, { target: { files: [file] } });
|
|
403
|
+
expect(onChange).toHaveBeenCalledTimes(2);
|
|
404
|
+
expect(onChange).toHaveBeenLastCalledWith(file);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('should call onChange twice when dropping the same file consecutively via drag & drop', () => {
|
|
408
|
+
const onChange = vi.fn();
|
|
409
|
+
render(
|
|
410
|
+
<IpponImportFile
|
|
411
|
+
title="Upload file"
|
|
412
|
+
description="Click to browse"
|
|
413
|
+
onChange={onChange}
|
|
414
|
+
dataSelector="ippon-import-file"
|
|
415
|
+
/>,
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
const file = fakeTextFile({ content: 'content', name: 'test.txt', type: 'text/plain' });
|
|
419
|
+
|
|
420
|
+
// First drop
|
|
421
|
+
fireEvent.drop(getIpponImportFile(), { dataTransfer: { files: [file] } });
|
|
422
|
+
expect(onChange).toHaveBeenCalledOnce();
|
|
423
|
+
expect(onChange).toHaveBeenCalledWith(file);
|
|
424
|
+
|
|
425
|
+
// Second drop of the same file
|
|
426
|
+
fireEvent.drop(getIpponImportFile(), { dataTransfer: { files: [file] } });
|
|
427
|
+
expect(onChange).toHaveBeenCalledTimes(2);
|
|
428
|
+
expect(onChange).toHaveBeenLastCalledWith(file);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { cleanup, configure, render, screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom/vitest';
|
|
4
|
+
import { IpponIon } from '../src';
|
|
5
|
+
|
|
6
|
+
configure({
|
|
7
|
+
testIdAttribute: 'data-selector',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('IpponIon', () => {
|
|
11
|
+
beforeEach(cleanup);
|
|
12
|
+
|
|
13
|
+
it('should be minimal', () => {
|
|
14
|
+
render(<IpponIon name="home" dataSelector="ippon-icon" />);
|
|
15
|
+
|
|
16
|
+
const ipponIcon = screen.getByTestId('ippon-icon');
|
|
17
|
+
|
|
18
|
+
expect(ipponIcon).toHaveRole('presentation');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should be filled', () => {
|
|
22
|
+
render(<IpponIon name="home" dataSelector="ippon-icon" />);
|
|
23
|
+
|
|
24
|
+
const ipponIcon = screen.getByTestId('ippon-icon');
|
|
25
|
+
|
|
26
|
+
expect(ipponIcon).toHaveClass('ippon-ion-home');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should be sharp', () => {
|
|
30
|
+
render(<IpponIon name="home" variant="sharp" dataSelector="ippon-icon" />);
|
|
31
|
+
|
|
32
|
+
const ipponIcon = screen.getByTestId('ippon-icon');
|
|
33
|
+
|
|
34
|
+
expect(ipponIcon).toHaveClass('ippon-ion-home-sharp');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should be outlined', () => {
|
|
38
|
+
render(<IpponIon name="home" variant="outline" dataSelector="ippon-icon" />);
|
|
39
|
+
|
|
40
|
+
const ipponIcon = screen.getByTestId('ippon-icon');
|
|
41
|
+
|
|
42
|
+
expect(ipponIcon).toHaveClass('ippon-ion-home-outline');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should be a logo', () => {
|
|
46
|
+
render(<IpponIon name="logo-ionic" dataSelector="ippon-icon" />);
|
|
47
|
+
|
|
48
|
+
const ipponIcon = screen.getByTestId('ippon-icon');
|
|
49
|
+
|
|
50
|
+
expect(ipponIcon).toHaveClass('ippon-ion-logo-ionic');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { IpponMeter } from '../src';
|
|
3
|
+
import { render, screen, configure, cleanup } from '@testing-library/react';
|
|
4
|
+
import '@testing-library/jest-dom/vitest';
|
|
5
|
+
|
|
6
|
+
configure({
|
|
7
|
+
testIdAttribute: 'data-selector',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const expectMeterToHave = ({
|
|
11
|
+
value,
|
|
12
|
+
min,
|
|
13
|
+
max,
|
|
14
|
+
percentage,
|
|
15
|
+
label,
|
|
16
|
+
}: {
|
|
17
|
+
value: string;
|
|
18
|
+
min: string;
|
|
19
|
+
max: string;
|
|
20
|
+
percentage: string;
|
|
21
|
+
label: string;
|
|
22
|
+
}) => {
|
|
23
|
+
const ipponMeter = screen.getByTestId('ippon-meter');
|
|
24
|
+
|
|
25
|
+
expect(ipponMeter).toHaveAttribute('aria-label', label);
|
|
26
|
+
expect(ipponMeter).toHaveClass('ippon-meter');
|
|
27
|
+
expect(ipponMeter).toHaveAttribute('aria-valuenow', value);
|
|
28
|
+
expect(ipponMeter).toHaveAttribute('aria-valuemin', min);
|
|
29
|
+
expect(ipponMeter).toHaveAttribute('aria-valuemax', max);
|
|
30
|
+
expect(ipponMeter).toHaveStyle(`--ippon-meter-percentage: ${percentage}`);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe('IpponMeter', () => {
|
|
34
|
+
afterEach(cleanup);
|
|
35
|
+
|
|
36
|
+
it('Should have 0 everywhere with min, max and value at 0', () => {
|
|
37
|
+
render(<IpponMeter value={0} min={0} max={0} label="Meter" dataSelector="ippon-meter" />);
|
|
38
|
+
|
|
39
|
+
expectMeterToHave({
|
|
40
|
+
label: 'Meter',
|
|
41
|
+
value: '0',
|
|
42
|
+
min: '0',
|
|
43
|
+
max: '0',
|
|
44
|
+
percentage: '0%',
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should have 50% for 11 on 22', () => {
|
|
49
|
+
render(<IpponMeter value={11} min={0} max={22} label="Meter" dataSelector="ippon-meter" />);
|
|
50
|
+
|
|
51
|
+
expectMeterToHave({
|
|
52
|
+
label: 'Meter',
|
|
53
|
+
value: '11',
|
|
54
|
+
min: '0',
|
|
55
|
+
max: '22',
|
|
56
|
+
percentage: '50%',
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { IpponProgress } from '../src';
|
|
3
|
+
import { render, screen, configure, cleanup } from '@testing-library/react';
|
|
4
|
+
import '@testing-library/jest-dom/vitest';
|
|
5
|
+
|
|
6
|
+
configure({
|
|
7
|
+
testIdAttribute: 'data-selector',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const expectProgressToHave = ({
|
|
11
|
+
value,
|
|
12
|
+
min,
|
|
13
|
+
max,
|
|
14
|
+
percentage,
|
|
15
|
+
label,
|
|
16
|
+
busy,
|
|
17
|
+
}: {
|
|
18
|
+
value: string;
|
|
19
|
+
min: string;
|
|
20
|
+
max: string;
|
|
21
|
+
percentage: string;
|
|
22
|
+
label: string;
|
|
23
|
+
busy: string;
|
|
24
|
+
}) => {
|
|
25
|
+
const ipponProgress = screen.getByTestId('ippon-progress');
|
|
26
|
+
|
|
27
|
+
expect(ipponProgress).toHaveAttribute('aria-label', label);
|
|
28
|
+
expect(ipponProgress).toHaveClass('ippon-progress');
|
|
29
|
+
expect(ipponProgress).toHaveAttribute('role', 'progressbar');
|
|
30
|
+
expect(ipponProgress).toHaveAttribute('aria-valuenow', value);
|
|
31
|
+
expect(ipponProgress).toHaveAttribute('aria-valuemin', min);
|
|
32
|
+
expect(ipponProgress).toHaveAttribute('aria-valuemax', max);
|
|
33
|
+
expect(ipponProgress).toHaveAttribute('aria-busy', busy);
|
|
34
|
+
expect(ipponProgress).toHaveStyle(`--ippon-progress-percentage: ${percentage}`);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe('IpponProgress', () => {
|
|
38
|
+
afterEach(cleanup);
|
|
39
|
+
|
|
40
|
+
it.each([
|
|
41
|
+
{ value: 0, min: 0, max: 0, expectedPercentage: '0%', expectedBusy: 'false' },
|
|
42
|
+
{ value: 11, min: 0, max: 22, expectedPercentage: '50%', expectedBusy: 'true' },
|
|
43
|
+
{ value: 22, min: 0, max: 22, expectedPercentage: '100%', expectedBusy: 'false' },
|
|
44
|
+
{ value: 30, min: 10, max: 50, expectedPercentage: '50%', expectedBusy: 'true' },
|
|
45
|
+
])(
|
|
46
|
+
'should have $expectedPercentage for value $value with min $min and max $max',
|
|
47
|
+
({ value, min, max, expectedPercentage, expectedBusy }) => {
|
|
48
|
+
render(
|
|
49
|
+
<IpponProgress
|
|
50
|
+
value={value}
|
|
51
|
+
min={min}
|
|
52
|
+
max={max}
|
|
53
|
+
label="Progress"
|
|
54
|
+
dataSelector="ippon-progress"
|
|
55
|
+
/>,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expectProgressToHave({
|
|
59
|
+
label: 'Progress',
|
|
60
|
+
value: String(value),
|
|
61
|
+
min: String(min),
|
|
62
|
+
max: String(max),
|
|
63
|
+
percentage: expectedPercentage,
|
|
64
|
+
busy: expectedBusy,
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
});
|