@semiont/react-ui 0.2.35 → 0.2.37
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/dist/index.d.mts +8 -0
- package/dist/index.mjs +252 -166
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/components/CodeMirrorRenderer.tsx +71 -203
- package/src/components/__tests__/AnnotateReferencesProgressWidget.test.tsx +142 -0
- package/src/components/__tests__/LiveRegion.hooks.test.tsx +79 -0
- package/src/components/__tests__/ResizeHandle.test.tsx +165 -0
- package/src/components/__tests__/SessionExpiryBanner.test.tsx +123 -0
- package/src/components/__tests__/StatusDisplay.test.tsx +160 -0
- package/src/components/__tests__/Toolbar.test.tsx +110 -0
- package/src/components/annotation-popups/__tests__/JsonLdView.test.tsx +285 -0
- package/src/components/annotation-popups/__tests__/SharedPopupElements.test.tsx +273 -0
- package/src/components/modals/__tests__/KeyboardShortcutsHelpModal.test.tsx +90 -0
- package/src/components/modals/__tests__/ProposeEntitiesModal.test.tsx +129 -0
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +180 -0
- package/src/components/navigation/__tests__/ObservableLink.test.tsx +90 -0
- package/src/components/navigation/__tests__/SimpleNavigation.test.tsx +169 -0
- package/src/components/navigation/__tests__/SortableResourceTab.test.tsx +371 -0
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +2 -0
- package/src/components/resource/AnnotateView.tsx +27 -153
- package/src/components/resource/__tests__/AnnotationHistory.test.tsx +349 -0
- package/src/components/resource/__tests__/HistoryEvent.test.tsx +492 -0
- package/src/components/resource/__tests__/event-formatting.test.ts +273 -0
- package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +226 -0
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +188 -0
- package/src/components/resource/panels/__tests__/PanelHeader.test.tsx +69 -0
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +445 -0
- package/src/components/resource/panels/__tests__/StatisticsPanel.test.tsx +271 -0
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +210 -0
- package/src/components/settings/__tests__/SettingsPanel.test.tsx +190 -0
- package/src/components/viewers/__tests__/ImageViewer.test.tsx +63 -0
- package/src/integrations/__tests__/css-modules-helper.test.tsx +225 -0
- package/src/integrations/__tests__/styled-components-theme.test.ts +179 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { screen } from '@testing-library/react';
|
|
4
|
+
import '@testing-library/jest-dom';
|
|
5
|
+
import { renderWithProviders } from '../../../test-utils';
|
|
6
|
+
import { ImageViewer } from '../ImageViewer';
|
|
7
|
+
import type { ResourceUri } from '@semiont/core';
|
|
8
|
+
|
|
9
|
+
describe('ImageViewer', () => {
|
|
10
|
+
const defaultProps = {
|
|
11
|
+
resourceUri: 'http://example.com/resources/abc-123' as ResourceUri,
|
|
12
|
+
mimeType: 'image/png',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
it('should render an img element with correct src derived from URI', () => {
|
|
16
|
+
renderWithProviders(<ImageViewer {...defaultProps} />);
|
|
17
|
+
|
|
18
|
+
const img = screen.getByRole('img');
|
|
19
|
+
expect(img).toHaveAttribute('src', '/api/resources/abc-123');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should use default alt text when none provided', () => {
|
|
23
|
+
renderWithProviders(<ImageViewer {...defaultProps} />);
|
|
24
|
+
|
|
25
|
+
const img = screen.getByRole('img');
|
|
26
|
+
expect(img).toHaveAttribute('alt', 'Resource image');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should use custom alt text when provided', () => {
|
|
30
|
+
renderWithProviders(
|
|
31
|
+
<ImageViewer {...defaultProps} alt="A beautiful diagram" />
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const img = screen.getByRole('img');
|
|
35
|
+
expect(img).toHaveAttribute('alt', 'A beautiful diagram');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should extract the last segment of the URI as resource ID', () => {
|
|
39
|
+
renderWithProviders(
|
|
40
|
+
<ImageViewer
|
|
41
|
+
resourceUri={'http://example.com/deep/path/to/resource-xyz' as ResourceUri}
|
|
42
|
+
mimeType="image/jpeg"
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const img = screen.getByRole('img');
|
|
47
|
+
expect(img).toHaveAttribute('src', '/api/resources/resource-xyz');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should render with correct class names', () => {
|
|
51
|
+
const { container } = renderWithProviders(<ImageViewer {...defaultProps} />);
|
|
52
|
+
|
|
53
|
+
expect(container.querySelector('.semiont-image-viewer')).toBeInTheDocument();
|
|
54
|
+
expect(container.querySelector('.semiont-image-viewer__image')).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should set imageRendering style to auto', () => {
|
|
58
|
+
renderWithProviders(<ImageViewer {...defaultProps} />);
|
|
59
|
+
|
|
60
|
+
const img = screen.getByRole('img');
|
|
61
|
+
expect(img).toHaveStyle({ imageRendering: 'auto' });
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import {
|
|
5
|
+
createSemiontClassName,
|
|
6
|
+
generateDataAttributes,
|
|
7
|
+
mergeDataAttributes,
|
|
8
|
+
withCSSModules,
|
|
9
|
+
cssModulesConfig,
|
|
10
|
+
defineCSSModules,
|
|
11
|
+
} from '../css-modules-helper';
|
|
12
|
+
|
|
13
|
+
describe('css-modules-helper', () => {
|
|
14
|
+
describe('createSemiontClassName', () => {
|
|
15
|
+
it('returns base class when no module match', () => {
|
|
16
|
+
const build = createSemiontClassName({}, 'semiont-button');
|
|
17
|
+
expect(build({})).toBe('semiont-button');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('adds CSS module class when available', () => {
|
|
21
|
+
const styles = { 'semiont-button': 'abc123' };
|
|
22
|
+
const build = createSemiontClassName(styles, 'semiont-button');
|
|
23
|
+
const result = build({});
|
|
24
|
+
expect(result).toContain('semiont-button');
|
|
25
|
+
expect(result).toContain('abc123');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('appends additional classes', () => {
|
|
29
|
+
const build = createSemiontClassName({}, 'semiont-button');
|
|
30
|
+
const result = build({}, 'extra-class');
|
|
31
|
+
expect(result).toContain('semiont-button');
|
|
32
|
+
expect(result).toContain('extra-class');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('generateDataAttributes', () => {
|
|
37
|
+
it('generates data attributes from props', () => {
|
|
38
|
+
const attrs = generateDataAttributes({
|
|
39
|
+
variant: 'primary',
|
|
40
|
+
size: 'md',
|
|
41
|
+
loading: true,
|
|
42
|
+
fullWidth: true,
|
|
43
|
+
iconOnly: true,
|
|
44
|
+
active: true,
|
|
45
|
+
disabled: true,
|
|
46
|
+
orientation: 'horizontal',
|
|
47
|
+
attached: true,
|
|
48
|
+
spacing: 'md',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(attrs['data-variant']).toBe('primary');
|
|
52
|
+
expect(attrs['data-size']).toBe('md');
|
|
53
|
+
expect(attrs['data-loading']).toBe('true');
|
|
54
|
+
expect(attrs['data-full-width']).toBe('true');
|
|
55
|
+
expect(attrs['data-icon-only']).toBe('true');
|
|
56
|
+
expect(attrs['data-active']).toBe('true');
|
|
57
|
+
expect(attrs['data-disabled']).toBe('true');
|
|
58
|
+
expect(attrs['data-orientation']).toBe('horizontal');
|
|
59
|
+
expect(attrs['data-attached']).toBe('true');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('omits undefined values for falsy booleans', () => {
|
|
63
|
+
const attrs = generateDataAttributes({
|
|
64
|
+
loading: false,
|
|
65
|
+
fullWidth: false,
|
|
66
|
+
iconOnly: false,
|
|
67
|
+
active: false,
|
|
68
|
+
disabled: false,
|
|
69
|
+
attached: false,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(attrs['data-loading']).toBeUndefined();
|
|
73
|
+
expect(attrs['data-full-width']).toBeUndefined();
|
|
74
|
+
expect(attrs['data-icon-only']).toBeUndefined();
|
|
75
|
+
expect(attrs['data-active']).toBeUndefined();
|
|
76
|
+
expect(attrs['data-disabled']).toBeUndefined();
|
|
77
|
+
expect(attrs['data-attached']).toBeUndefined();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('omits spacing when attached is true', () => {
|
|
81
|
+
const attrs = generateDataAttributes({
|
|
82
|
+
attached: true,
|
|
83
|
+
spacing: 'lg',
|
|
84
|
+
});
|
|
85
|
+
expect(attrs['data-spacing']).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('includes spacing when attached is false', () => {
|
|
89
|
+
const attrs = generateDataAttributes({
|
|
90
|
+
attached: false,
|
|
91
|
+
spacing: 'lg',
|
|
92
|
+
});
|
|
93
|
+
expect(attrs['data-spacing']).toBe('lg');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('mergeDataAttributes', () => {
|
|
98
|
+
it('merges data attributes into props', () => {
|
|
99
|
+
const props = { id: 'btn', className: 'foo' };
|
|
100
|
+
const dataAttrs = { 'data-variant': 'primary', 'data-size': 'md' };
|
|
101
|
+
const merged = mergeDataAttributes(props, dataAttrs);
|
|
102
|
+
|
|
103
|
+
expect(merged.id).toBe('btn');
|
|
104
|
+
expect(merged.className).toBe('foo');
|
|
105
|
+
expect(merged['data-variant']).toBe('primary');
|
|
106
|
+
expect(merged['data-size']).toBe('md');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('skips undefined data attributes', () => {
|
|
110
|
+
const props = { id: 'btn' };
|
|
111
|
+
const dataAttrs = { 'data-variant': 'primary', 'data-size': undefined };
|
|
112
|
+
const merged = mergeDataAttributes(props, dataAttrs);
|
|
113
|
+
|
|
114
|
+
expect(merged['data-variant']).toBe('primary');
|
|
115
|
+
expect(merged).not.toHaveProperty('data-size');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('does not mutate original props', () => {
|
|
119
|
+
const props = { id: 'btn' };
|
|
120
|
+
mergeDataAttributes(props, { 'data-x': 'y' });
|
|
121
|
+
expect(props).not.toHaveProperty('data-x');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('withCSSModules', () => {
|
|
126
|
+
it('wraps a component with CSS module classes', () => {
|
|
127
|
+
const Inner = (props: { className?: string }) => (
|
|
128
|
+
<span data-testid="inner" className={props.className} />
|
|
129
|
+
);
|
|
130
|
+
const styles = { button: 'hashed_button' };
|
|
131
|
+
const Wrapped = withCSSModules(Inner, styles, 'button');
|
|
132
|
+
|
|
133
|
+
const { getByTestId } = render(<Wrapped />);
|
|
134
|
+
const el = getByTestId('inner');
|
|
135
|
+
expect(el.className).toContain('button');
|
|
136
|
+
expect(el.className).toContain('hashed_button');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('preserves existing className from props', () => {
|
|
140
|
+
const Inner = (props: { className?: string }) => (
|
|
141
|
+
<span data-testid="inner" className={props.className} />
|
|
142
|
+
);
|
|
143
|
+
const styles = { button: 'hashed' };
|
|
144
|
+
const Wrapped = withCSSModules(Inner, styles, 'button');
|
|
145
|
+
|
|
146
|
+
const { getByTestId } = render(<Wrapped className="custom" />);
|
|
147
|
+
const el = getByTestId('inner');
|
|
148
|
+
expect(el.className).toContain('custom');
|
|
149
|
+
expect(el.className).toContain('hashed');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('passes through other props', () => {
|
|
153
|
+
const Inner = (props: { className?: string; 'data-testid'?: string; title?: string }) => (
|
|
154
|
+
<span data-testid={props['data-testid']} title={props.title} />
|
|
155
|
+
);
|
|
156
|
+
const Wrapped = withCSSModules(Inner, {}, 'btn');
|
|
157
|
+
|
|
158
|
+
const { getByTestId } = render(<Wrapped data-testid="test" title="hello" />);
|
|
159
|
+
expect(getByTestId('test')).toHaveAttribute('title', 'hello');
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('cssModulesConfig', () => {
|
|
164
|
+
it('has postcss config', () => {
|
|
165
|
+
expect(cssModulesConfig.postcss).toBeDefined();
|
|
166
|
+
expect(cssModulesConfig.postcss.plugins).toHaveLength(1);
|
|
167
|
+
expect(cssModulesConfig.postcss.plugins[0].postcssPlugin).toBe('preserve-data-attributes');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('has webpack config with getLocalIdent', () => {
|
|
171
|
+
expect(cssModulesConfig.webpack.cssLoader.modules.localIdentName).toBeDefined();
|
|
172
|
+
const getLocalIdent = cssModulesConfig.webpack.cssLoader.modules.getLocalIdent;
|
|
173
|
+
expect(typeof getLocalIdent).toBe('function');
|
|
174
|
+
|
|
175
|
+
// semiont- prefixed classes are preserved
|
|
176
|
+
expect(getLocalIdent(null, '', 'semiont-button')).toBe('semiont-button');
|
|
177
|
+
// other classes return null for default hashing
|
|
178
|
+
expect(getLocalIdent(null, '', 'my-class')).toBeNull();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('has vite config', () => {
|
|
182
|
+
expect(cssModulesConfig.vite.css.modules.scopeBehaviour).toBe('local');
|
|
183
|
+
expect(cssModulesConfig.vite.css.modules.generateScopedName).toBeDefined();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('postcss plugin', () => {
|
|
187
|
+
it('wraps class selectors in :global when rule contains [data-', () => {
|
|
188
|
+
const plugin = cssModulesConfig.postcss.plugins[0];
|
|
189
|
+
const rules: any[] = [];
|
|
190
|
+
const mockRoot = {
|
|
191
|
+
walkRules: (cb: (rule: any) => void) => {
|
|
192
|
+
for (const rule of rules) cb(rule);
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// Rule with data-attribute selector
|
|
197
|
+
const rule = { selector: '.button[data-variant="primary"]' };
|
|
198
|
+
rules.push(rule);
|
|
199
|
+
plugin.Once(mockRoot);
|
|
200
|
+
|
|
201
|
+
expect(rule.selector).toContain(':global(.button)');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('does not modify rules without data-attribute selectors', () => {
|
|
205
|
+
const plugin = cssModulesConfig.postcss.plugins[0];
|
|
206
|
+
const rule = { selector: '.button .icon' };
|
|
207
|
+
const mockRoot = {
|
|
208
|
+
walkRules: (cb: (rule: any) => void) => cb(rule),
|
|
209
|
+
};
|
|
210
|
+
plugin.Once(mockRoot);
|
|
211
|
+
expect(rule.selector).toBe('.button .icon');
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe('defineCSSModules', () => {
|
|
217
|
+
it('returns the styles object typed', () => {
|
|
218
|
+
const raw = { button: 'abc', 'button-content': 'def' };
|
|
219
|
+
const typed = defineCSSModules(raw);
|
|
220
|
+
expect(typed).toBe(raw);
|
|
221
|
+
expect(typed.button).toBe('abc');
|
|
222
|
+
expect(typed['button-content']).toBe('def');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { semiontTheme, semiontMixins, createStyledSemiontButton } from '../styled-components-theme';
|
|
3
|
+
import { tokens } from '../../design-tokens';
|
|
4
|
+
|
|
5
|
+
describe('styled-components-theme', () => {
|
|
6
|
+
describe('semiontTheme', () => {
|
|
7
|
+
it('includes all token categories', () => {
|
|
8
|
+
expect(semiontTheme).toHaveProperty('colors');
|
|
9
|
+
expect(semiontTheme).toHaveProperty('spacing');
|
|
10
|
+
expect(semiontTheme).toHaveProperty('typography');
|
|
11
|
+
expect(semiontTheme).toHaveProperty('borderRadius');
|
|
12
|
+
expect(semiontTheme).toHaveProperty('shadows');
|
|
13
|
+
expect(semiontTheme).toHaveProperty('transitions');
|
|
14
|
+
expect(semiontTheme).toHaveProperty('breakpoints');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('spacing matches tokens.spacing', () => {
|
|
18
|
+
expect(semiontTheme.spacing).toBe(tokens.spacing);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('typography matches tokens.typography', () => {
|
|
22
|
+
expect(semiontTheme.typography).toBe(tokens.typography);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('borderRadius matches tokens.borderRadius', () => {
|
|
26
|
+
expect(semiontTheme.borderRadius).toBe(tokens.borderRadius);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('shadows matches tokens.shadows', () => {
|
|
30
|
+
expect(semiontTheme.shadows).toBe(tokens.shadows);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('transitions matches tokens.transitions', () => {
|
|
34
|
+
expect(semiontTheme.transitions).toBe(tokens.transitions);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('breakpoints matches tokens.breakpoints', () => {
|
|
38
|
+
expect(semiontTheme.breakpoints).toBe(tokens.breakpoints);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('flattens semantic colors onto colors', () => {
|
|
42
|
+
expect(semiontTheme.colors.error).toBe(tokens.colors.semantic.error);
|
|
43
|
+
expect(semiontTheme.colors.errorLight).toBe(tokens.colors.semantic.errorLight);
|
|
44
|
+
expect(semiontTheme.colors.errorDark).toBe(tokens.colors.semantic.errorDark);
|
|
45
|
+
expect(semiontTheme.colors.warning).toBe(tokens.colors.semantic.warning);
|
|
46
|
+
expect(semiontTheme.colors.warningLight).toBe(tokens.colors.semantic.warningLight);
|
|
47
|
+
expect(semiontTheme.colors.warningDark).toBe(tokens.colors.semantic.warningDark);
|
|
48
|
+
expect(semiontTheme.colors.success).toBe(tokens.colors.semantic.success);
|
|
49
|
+
expect(semiontTheme.colors.successLight).toBe(tokens.colors.semantic.successLight);
|
|
50
|
+
expect(semiontTheme.colors.successDark).toBe(tokens.colors.semantic.successDark);
|
|
51
|
+
expect(semiontTheme.colors.info).toBe(tokens.colors.semantic.info);
|
|
52
|
+
expect(semiontTheme.colors.infoLight).toBe(tokens.colors.semantic.infoLight);
|
|
53
|
+
expect(semiontTheme.colors.infoDark).toBe(tokens.colors.semantic.infoDark);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('preserves nested color structures', () => {
|
|
57
|
+
expect(semiontTheme.colors.primary).toBe(tokens.colors.primary);
|
|
58
|
+
expect(semiontTheme.colors.secondary).toBe(tokens.colors.secondary);
|
|
59
|
+
expect(semiontTheme.colors.neutral).toBe(tokens.colors.neutral);
|
|
60
|
+
expect(semiontTheme.colors.semantic).toBe(tokens.colors.semantic);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('semiontMixins', () => {
|
|
65
|
+
it('has buttonBase mixin', () => {
|
|
66
|
+
expect(semiontMixins.buttonBase).toBeDefined();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('has buttonVariant function', () => {
|
|
70
|
+
expect(typeof semiontMixins.buttonVariant).toBe('function');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('buttonVariant accepts variant strings', () => {
|
|
74
|
+
for (const variant of ['primary', 'secondary', 'tertiary', 'danger', 'warning', 'ghost']) {
|
|
75
|
+
expect(semiontMixins.buttonVariant(variant)).toBeDefined();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('has buttonSize function', () => {
|
|
80
|
+
expect(typeof semiontMixins.buttonSize).toBe('function');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('buttonSize accepts size strings', () => {
|
|
84
|
+
for (const size of ['xs', 'sm', 'md', 'lg', 'xl']) {
|
|
85
|
+
expect(semiontMixins.buttonSize(size)).toBeDefined();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('has focusRing function', () => {
|
|
90
|
+
expect(typeof semiontMixins.focusRing).toBe('function');
|
|
91
|
+
expect(semiontMixins.focusRing()).toBeDefined();
|
|
92
|
+
expect(semiontMixins.focusRing('#ff0000')).toBeDefined();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('has truncate mixin', () => {
|
|
96
|
+
expect(semiontMixins.truncate).toBeDefined();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('has srOnly mixin', () => {
|
|
100
|
+
expect(semiontMixins.srOnly).toBeDefined();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('has media query helpers', () => {
|
|
104
|
+
expect(semiontMixins.media).toHaveProperty('sm');
|
|
105
|
+
expect(semiontMixins.media).toHaveProperty('md');
|
|
106
|
+
expect(semiontMixins.media).toHaveProperty('lg');
|
|
107
|
+
expect(semiontMixins.media).toHaveProperty('xl');
|
|
108
|
+
expect(semiontMixins.media).toHaveProperty('2xl');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('media helpers are functions', () => {
|
|
112
|
+
expect(typeof semiontMixins.media.sm).toBe('function');
|
|
113
|
+
expect(typeof semiontMixins.media.md).toBe('function');
|
|
114
|
+
expect(typeof semiontMixins.media.lg).toBe('function');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('createStyledSemiontButton', () => {
|
|
119
|
+
it('is a function', () => {
|
|
120
|
+
expect(typeof createStyledSemiontButton).toBe('function');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('calls styled.button.attrs', () => {
|
|
124
|
+
let attrsArg: any;
|
|
125
|
+
let templateArg: any;
|
|
126
|
+
|
|
127
|
+
const mockStyled = {
|
|
128
|
+
button: {
|
|
129
|
+
attrs: (arg: any) => {
|
|
130
|
+
attrsArg = arg;
|
|
131
|
+
return (strings: TemplateStringsArray, ...exprs: any[]) => {
|
|
132
|
+
templateArg = { strings, exprs };
|
|
133
|
+
return 'MockComponent';
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const result = createStyledSemiontButton(mockStyled);
|
|
140
|
+
expect(result).toBe('MockComponent');
|
|
141
|
+
expect(attrsArg).toBeDefined();
|
|
142
|
+
|
|
143
|
+
// Test the attrs function generates correct data attributes
|
|
144
|
+
const attrs = attrsArg({
|
|
145
|
+
variant: 'primary',
|
|
146
|
+
size: 'md',
|
|
147
|
+
loading: true,
|
|
148
|
+
fullWidth: true,
|
|
149
|
+
iconOnly: false,
|
|
150
|
+
active: true,
|
|
151
|
+
disabled: false,
|
|
152
|
+
className: 'custom',
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
expect(attrs['data-variant']).toBe('primary');
|
|
156
|
+
expect(attrs['data-size']).toBe('md');
|
|
157
|
+
expect(attrs['data-loading']).toBe('true');
|
|
158
|
+
expect(attrs['data-full-width']).toBe('true');
|
|
159
|
+
expect(attrs['data-icon-only']).toBeUndefined();
|
|
160
|
+
expect(attrs['data-active']).toBe('true');
|
|
161
|
+
expect(attrs['data-disabled']).toBeUndefined();
|
|
162
|
+
expect(attrs.className).toBe('semiont-button custom');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('attrs trims className when no additional class', () => {
|
|
166
|
+
const mockStyled = {
|
|
167
|
+
button: {
|
|
168
|
+
attrs: (arg: any) => {
|
|
169
|
+
const attrs = arg({});
|
|
170
|
+
expect(attrs.className).toBe('semiont-button');
|
|
171
|
+
return () => 'MockComponent';
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
createStyledSemiontButton(mockStyled);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|