@pie-lib/text-select 3.0.3-next.36 → 3.0.3-next.38

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.
Files changed (70) hide show
  1. package/dist/index.d.ts +15 -0
  2. package/dist/index.js +7 -0
  3. package/dist/legend.d.ts +13 -0
  4. package/dist/legend.js +64 -0
  5. package/dist/node_modules/.bun/clsx@2.1.1/node_modules/clsx/dist/clsx.js +16 -0
  6. package/dist/text-select.d.ts +34 -0
  7. package/dist/text-select.js +53 -0
  8. package/dist/token-select/index.d.ts +44 -0
  9. package/dist/token-select/index.js +170 -0
  10. package/dist/token-select/token.d.ts +32 -0
  11. package/dist/token-select/token.js +134 -0
  12. package/dist/tokenizer/builder.d.ts +27 -0
  13. package/dist/tokenizer/builder.js +124 -0
  14. package/dist/tokenizer/controls.d.ts +23 -0
  15. package/dist/tokenizer/controls.js +68 -0
  16. package/dist/tokenizer/index.d.ts +35 -0
  17. package/dist/tokenizer/index.js +91 -0
  18. package/dist/tokenizer/selection-utils.d.ts +10 -0
  19. package/dist/tokenizer/selection-utils.js +18 -0
  20. package/dist/tokenizer/token-text.d.ts +27 -0
  21. package/dist/tokenizer/token-text.js +85 -0
  22. package/dist/utils.d.ts +12 -0
  23. package/dist/utils.js +21 -0
  24. package/package.json +33 -30
  25. package/CHANGELOG.json +0 -1
  26. package/CHANGELOG.md +0 -946
  27. package/LICENSE.md +0 -5
  28. package/lib/index.js +0 -57
  29. package/lib/index.js.map +0 -1
  30. package/lib/legend.js +0 -119
  31. package/lib/legend.js.map +0 -1
  32. package/lib/text-select.js +0 -105
  33. package/lib/text-select.js.map +0 -1
  34. package/lib/token-select/index.js +0 -267
  35. package/lib/token-select/index.js.map +0 -1
  36. package/lib/token-select/token.js +0 -236
  37. package/lib/token-select/token.js.map +0 -1
  38. package/lib/tokenizer/builder.js +0 -265
  39. package/lib/tokenizer/builder.js.map +0 -1
  40. package/lib/tokenizer/controls.js +0 -106
  41. package/lib/tokenizer/controls.js.map +0 -1
  42. package/lib/tokenizer/index.js +0 -147
  43. package/lib/tokenizer/index.js.map +0 -1
  44. package/lib/tokenizer/selection-utils.js +0 -55
  45. package/lib/tokenizer/selection-utils.js.map +0 -1
  46. package/lib/tokenizer/token-text.js +0 -176
  47. package/lib/tokenizer/token-text.js.map +0 -1
  48. package/lib/utils.js +0 -51
  49. package/lib/utils.js.map +0 -1
  50. package/src/__tests__/legend.test.jsx +0 -211
  51. package/src/__tests__/text-select.test.jsx +0 -44
  52. package/src/__tests__/utils.test.jsx +0 -27
  53. package/src/index.js +0 -8
  54. package/src/legend.js +0 -102
  55. package/src/text-select.jsx +0 -79
  56. package/src/token-select/__tests__/index.test.jsx +0 -623
  57. package/src/token-select/__tests__/token.test.jsx +0 -236
  58. package/src/token-select/index.jsx +0 -242
  59. package/src/token-select/token.jsx +0 -223
  60. package/src/tokenizer/__tests__/builder.test.js +0 -256
  61. package/src/tokenizer/__tests__/controls.test.jsx +0 -27
  62. package/src/tokenizer/__tests__/index.test.jsx +0 -329
  63. package/src/tokenizer/__tests__/selection-utils.test.js +0 -145
  64. package/src/tokenizer/__tests__/token-text.test.jsx +0 -318
  65. package/src/tokenizer/builder.js +0 -258
  66. package/src/tokenizer/controls.jsx +0 -71
  67. package/src/tokenizer/index.jsx +0 -144
  68. package/src/tokenizer/selection-utils.js +0 -49
  69. package/src/tokenizer/token-text.jsx +0 -135
  70. package/src/utils.js +0 -56
@@ -1,211 +0,0 @@
1
- import React from 'react';
2
- import { render } from '@testing-library/react';
3
- import { Legend } from '../legend';
4
-
5
- jest.mock('@pie-lib/translator', () => ({
6
- __esModule: true,
7
- default: {
8
- translator: {
9
- t: jest.fn((key) => {
10
- const translations = {
11
- 'selectText.key': 'Key',
12
- 'selectText.correctAnswerSelected': 'Correct answer selected',
13
- 'selectText.incorrectSelection': 'Incorrect selection',
14
- 'selectText.correctAnswerNotSelected': 'Correct answer not selected',
15
- };
16
- return translations[key] || key;
17
- }),
18
- },
19
- },
20
- }));
21
-
22
- describe('Legend', () => {
23
- beforeEach(() => {
24
- jest.clearAllMocks();
25
- });
26
-
27
- describe('rendering', () => {
28
- it('renders without crashing', () => {
29
- const { container } = render(<Legend />);
30
- expect(container.firstChild).toBeInTheDocument();
31
- });
32
-
33
- it('renders the Key label', () => {
34
- const { getByText } = render(<Legend />);
35
- expect(getByText('Key')).toBeInTheDocument();
36
- });
37
-
38
- it('renders all three legend items by default', () => {
39
- const { getByText } = render(<Legend />);
40
-
41
- expect(getByText('Correct answer selected')).toBeInTheDocument();
42
- expect(getByText('Incorrect selection')).toBeInTheDocument();
43
- expect(getByText('Correct answer not selected')).toBeInTheDocument();
44
- });
45
-
46
- it('renders with custom language', () => {
47
- const Translator = require('@pie-lib/translator').default;
48
- const { container } = render(<Legend language="es" />);
49
-
50
- expect(Translator.translator.t).toHaveBeenCalledWith('selectText.key', { lng: 'es' });
51
- expect(container.firstChild).toBeInTheDocument();
52
- });
53
- });
54
-
55
- describe('showOnlyCorrect prop', () => {
56
- it('shows all three items when showOnlyCorrect is false', () => {
57
- const { getByText } = render(<Legend showOnlyCorrect={false} />);
58
-
59
- expect(getByText('Correct answer selected')).toBeInTheDocument();
60
- expect(getByText('Incorrect selection')).toBeInTheDocument();
61
- expect(getByText('Correct answer not selected')).toBeInTheDocument();
62
- });
63
-
64
- it('shows only correct item when showOnlyCorrect is true', () => {
65
- const { getByText, queryByText } = render(<Legend showOnlyCorrect={true} />);
66
-
67
- expect(getByText('Correct answer selected')).toBeInTheDocument();
68
- expect(queryByText('Incorrect selection')).not.toBeInTheDocument();
69
- expect(queryByText('Correct answer not selected')).not.toBeInTheDocument();
70
- });
71
-
72
- it('shows only correct item when showOnlyCorrect is undefined', () => {
73
- const { getByText } = render(<Legend showOnlyCorrect={undefined} />);
74
-
75
- expect(getByText('Correct answer selected')).toBeInTheDocument();
76
- expect(getByText('Incorrect selection')).toBeInTheDocument();
77
- expect(getByText('Correct answer not selected')).toBeInTheDocument();
78
- });
79
- });
80
-
81
- describe('icons', () => {
82
- it('renders check icons for correct answers', () => {
83
- const { container } = render(<Legend />);
84
- const checkIcons = container.querySelectorAll('svg[data-testid="CheckIcon"]');
85
- expect(checkIcons.length).toBeGreaterThan(0);
86
- });
87
-
88
- it('renders close icons for incorrect answers', () => {
89
- const { container } = render(<Legend />);
90
- const closeIcons = container.querySelectorAll('svg[data-testid="CloseIcon"]');
91
- expect(closeIcons.length).toBeGreaterThan(0);
92
- });
93
-
94
- it('renders correct number of icons when showOnlyCorrect is true', () => {
95
- const { container } = render(<Legend showOnlyCorrect={true} />);
96
- const checkIcons = container.querySelectorAll('svg[data-testid="CheckIcon"]');
97
- const closeIcons = container.querySelectorAll('svg[data-testid="CloseIcon"]');
98
-
99
- expect(checkIcons.length).toBe(1);
100
- expect(closeIcons.length).toBe(0);
101
- });
102
- });
103
-
104
- describe('styled containers', () => {
105
- it('renders containers for each legend item', () => {
106
- const { container } = render(<Legend />);
107
- const spans = container.querySelectorAll('span');
108
-
109
- expect(spans.length).toBeGreaterThanOrEqual(4);
110
- });
111
-
112
- it('applies correct styling classes', () => {
113
- const { container } = render(<Legend />);
114
-
115
- expect(container.firstChild).toBeInTheDocument();
116
- expect(container.firstChild).toHaveStyle({ display: 'flex' });
117
- });
118
- });
119
-
120
- describe('translation integration', () => {
121
- it('calls translator with correct keys for all items', () => {
122
- const Translator = require('@pie-lib/translator').default;
123
- render(<Legend />);
124
-
125
- expect(Translator.translator.t).toHaveBeenCalledWith('selectText.key', { lng: undefined });
126
- expect(Translator.translator.t).toHaveBeenCalledWith('selectText.correctAnswerSelected', { lng: undefined });
127
- expect(Translator.translator.t).toHaveBeenCalledWith('selectText.incorrectSelection', { lng: undefined });
128
- expect(Translator.translator.t).toHaveBeenCalledWith('selectText.correctAnswerNotSelected', { lng: undefined });
129
- });
130
-
131
- it('passes language prop to all translation calls', () => {
132
- const Translator = require('@pie-lib/translator').default;
133
- render(<Legend language="fr" />);
134
-
135
- expect(Translator.translator.t).toHaveBeenCalledWith('selectText.key', { lng: 'fr' });
136
- expect(Translator.translator.t).toHaveBeenCalledWith('selectText.correctAnswerSelected', { lng: 'fr' });
137
- });
138
-
139
- it('handles missing language prop gracefully', () => {
140
- const Translator = require('@pie-lib/translator').default;
141
- render(<Legend />);
142
-
143
- expect(Translator.translator.t).toHaveBeenCalledWith('selectText.key', { lng: undefined });
144
- });
145
- });
146
-
147
- describe('legend items structure', () => {
148
- it('renders items with correct structure', () => {
149
- const { container } = render(<Legend />);
150
-
151
- const legendItems = container.querySelectorAll('div > div');
152
- expect(legendItems.length).toBeGreaterThanOrEqual(3);
153
- });
154
-
155
- it('renders items in correct order', () => {
156
- const { container } = render(<Legend />);
157
- const spans = Array.from(container.querySelectorAll('span')).map((s) => s.textContent);
158
-
159
- expect(spans).toContain('Correct answer selected');
160
- expect(spans).toContain('Incorrect selection');
161
- expect(spans).toContain('Correct answer not selected');
162
- });
163
-
164
- it('maintains order when showOnlyCorrect is true', () => {
165
- const { container } = render(<Legend showOnlyCorrect={true} />);
166
- const spans = Array.from(container.querySelectorAll('span')).map((s) => s.textContent);
167
-
168
- expect(spans).toContain('Correct answer selected');
169
- expect(spans).not.toContain('Incorrect selection');
170
- });
171
- });
172
-
173
- describe('prop combinations', () => {
174
- it('handles both language and showOnlyCorrect props together', () => {
175
- const { getByText, queryByText } = render(<Legend language="de" showOnlyCorrect={true} />);
176
-
177
- expect(getByText('Correct answer selected')).toBeInTheDocument();
178
- expect(queryByText('Incorrect selection')).not.toBeInTheDocument();
179
- });
180
-
181
- it('renders correctly with no props', () => {
182
- const { container } = render(<Legend />);
183
- expect(container.firstChild).toBeInTheDocument();
184
- });
185
-
186
- it('renders correctly with empty language string', () => {
187
- const { container } = render(<Legend language="" />);
188
- expect(container.firstChild).toBeInTheDocument();
189
- });
190
- });
191
-
192
- describe('accessibility', () => {
193
- it('renders semantic HTML structure', () => {
194
- const { container } = render(<Legend />);
195
-
196
- const divs = container.querySelectorAll('div');
197
- const spans = container.querySelectorAll('span');
198
-
199
- expect(divs.length).toBeGreaterThan(0);
200
- expect(spans.length).toBeGreaterThan(0);
201
- });
202
-
203
- it('includes text labels for each icon', () => {
204
- const { getByText } = render(<Legend />);
205
-
206
- expect(getByText('Correct answer selected')).toBeVisible();
207
- expect(getByText('Incorrect selection')).toBeVisible();
208
- expect(getByText('Correct answer not selected')).toBeVisible();
209
- });
210
- });
211
- });
@@ -1,44 +0,0 @@
1
- import TextSelect from '../text-select';
2
- import React from 'react';
3
- import { render } from '@testing-library/react';
4
-
5
- describe('text-select', () => {
6
- const defaultProps = {
7
- text: 'foo',
8
- tokens: [],
9
- selectedTokens: [],
10
- onChange: jest.fn(),
11
- };
12
-
13
- describe('rendering', () => {
14
- it('renders with default props', () => {
15
- const { container } = render(<TextSelect {...defaultProps} />);
16
- expect(container.firstChild).toBeInTheDocument();
17
- });
18
-
19
- it('renders with maxNoOfSelections', () => {
20
- const { container } = render(<TextSelect {...defaultProps} maxNoOfSelections={4} />);
21
- expect(container.firstChild).toBeInTheDocument();
22
- });
23
-
24
- it('renders with tokens', () => {
25
- const tokens = [
26
- { start: 0, end: 1, text: 'f' },
27
- { start: 1, end: 2, text: 'o' },
28
- ];
29
- const { container } = render(<TextSelect {...defaultProps} tokens={tokens} />);
30
- expect(container.firstChild).toBeInTheDocument();
31
- });
32
-
33
- it('renders with selectedTokens', () => {
34
- const selectedTokens = [{ start: 0, end: 1, text: 'f' }];
35
- const { container } = render(<TextSelect {...defaultProps} selectedTokens={selectedTokens} />);
36
- expect(container.firstChild).toBeInTheDocument();
37
- });
38
- });
39
-
40
- // Note: Tests for internal method (change) are implementation details and cannot
41
- // be directly tested with RTL. The original test used wrapper.instance().change()
42
- // to test internal logic, which tests implementation rather than user-facing behavior.
43
- // Token selection behavior should be tested through integration/e2e tests.
44
- });
@@ -1,27 +0,0 @@
1
- import { prepareText } from '../utils';
2
-
3
- describe('logic', () => {
4
- it('returns text if no html elements', () => {
5
- const formattedText = prepareText(`foo bar`);
6
-
7
- expect(formattedText).toEqual('foo bar');
8
- });
9
-
10
- it('replaces br with new lines', () => {
11
- const formattedText = prepareText(`<p>foo<br>bar</p>`);
12
-
13
- expect(formattedText).toEqual('foo\nbar');
14
- });
15
-
16
- it('replaces p with 2 new lines', () => {
17
- const formattedText = prepareText(`<p>foo<br>bar</p><p>bar<br>foo</p>`);
18
-
19
- expect(formattedText).toEqual('foo\nbar\n\nbar\nfoo');
20
- });
21
-
22
- it('adds p if there are no paragraphs', () => {
23
- const formattedText = prepareText(`foo<br>bar<br>foo`);
24
-
25
- expect(formattedText).toEqual('foo\nbar\nfoo');
26
- });
27
- });
package/src/index.js DELETED
@@ -1,8 +0,0 @@
1
- import Tokenizer from './tokenizer';
2
- import TokenSelect, { TokenTypes } from './token-select';
3
- import TextSelect from './text-select';
4
- import { Legend } from './legend';
5
- import Token from './token-select/token';
6
- import { prepareText } from './utils';
7
-
8
- export { TextSelect, TokenTypes, Tokenizer, TokenSelect, Token, prepareText, Legend };
package/src/legend.js DELETED
@@ -1,102 +0,0 @@
1
- import React from 'react';
2
- import { styled } from '@mui/material/styles';
3
- import Check from '@mui/icons-material/Check';
4
- import Close from '@mui/icons-material/Close';
5
- import { color } from '@pie-lib/render-ui';
6
- import Translator from '@pie-lib/translator';
7
-
8
- const { translator } = Translator;
9
-
10
- const StyledFlexContainer = styled('div')(({ theme }) => ({
11
- display: 'flex',
12
- flexDirection: 'row',
13
- alignItems: 'center',
14
- gap: theme.spacing(2),
15
- borderBottom: '1px solid lightgrey',
16
- borderTop: '1px solid lightgrey',
17
- paddingBottom: theme.spacing(1),
18
- paddingTop: theme.spacing(1),
19
- marginBottom: theme.spacing(1),
20
- }));
21
-
22
- const StyledKey = styled('span')(({ theme }) => ({
23
- fontSize: '14px',
24
- fontWeight: 'bold',
25
- color: color.black(),
26
- marginLeft: theme.spacing(1),
27
- }));
28
-
29
- const StyledContainer = styled('div')(() => ({
30
- position: 'relative',
31
- padding: '4px',
32
- fontSize: '14px',
33
- borderRadius: '4px',
34
- }));
35
-
36
- const StyledCorrectContainer = styled(StyledContainer)(() => ({
37
- border: `${color.correctTertiary()} solid 2px`,
38
- }));
39
-
40
- const StyledIncorrectContainer = styled(StyledContainer)(() => ({
41
- border: `${color.incorrectWithIcon()} solid 2px`,
42
- }));
43
-
44
- const StyledMissingContainer = styled(StyledContainer)(() => ({
45
- border: `${color.incorrectWithIcon()} dashed 2px`,
46
- }));
47
-
48
- const baseIconStyles = {
49
- color: color.white(),
50
- position: 'absolute',
51
- top: '-8px',
52
- left: '-8px',
53
- borderRadius: '50%',
54
- fontSize: '12px',
55
- padding: '2px',
56
- };
57
-
58
- const StyledCorrectCheckIcon = styled(Check)(() => ({
59
- ...baseIconStyles,
60
- backgroundColor: color.correctTertiary(),
61
- }));
62
-
63
- const StyledIncorrectCloseIcon = styled(Close)(() => ({
64
- ...baseIconStyles,
65
- backgroundColor: color.incorrectWithIcon(),
66
- }));
67
-
68
- export const Legend = ({ language, showOnlyCorrect }) => {
69
- const legendItems = [
70
- {
71
- Icon: StyledCorrectCheckIcon,
72
- label: translator.t('selectText.correctAnswerSelected', { lng: language }),
73
- Container: StyledCorrectContainer,
74
- },
75
- {
76
- Icon: StyledIncorrectCloseIcon,
77
- label: translator.t('selectText.incorrectSelection', { lng: language }),
78
- Container: StyledIncorrectContainer,
79
- },
80
- {
81
- Icon: StyledIncorrectCloseIcon,
82
- label: translator.t('selectText.correctAnswerNotSelected', { lng: language }),
83
- Container: StyledMissingContainer,
84
- },
85
- ];
86
-
87
- if (showOnlyCorrect) {
88
- legendItems.splice(1, 2);
89
- }
90
-
91
- return (
92
- <StyledFlexContainer>
93
- <StyledKey>{translator.t('selectText.key', { lng: language })}</StyledKey>
94
- {legendItems.map(({ Icon, label, Container }, idx) => (
95
- <Container key={idx}>
96
- <Icon />
97
- <span>{label}</span>
98
- </Container>
99
- ))}
100
- </StyledFlexContainer>
101
- );
102
- };
@@ -1,79 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
-
4
- import TokenSelect from './token-select';
5
- import { normalize } from './tokenizer/builder';
6
- import { TokenTypes } from './token-select/token';
7
- import debug from 'debug';
8
-
9
- const log = debug('@pie-lib:text-select');
10
- /**
11
- * Built on TokenSelect uses build.normalize to build the token set.
12
- */
13
- export default class TextSelect extends React.Component {
14
- static propTypes = {
15
- onChange: PropTypes.func,
16
- disabled: PropTypes.bool,
17
- tokens: PropTypes.arrayOf(PropTypes.shape(TokenTypes)).isRequired,
18
- selectedTokens: PropTypes.arrayOf(PropTypes.shape(TokenTypes)).isRequired,
19
- text: PropTypes.string.isRequired,
20
- className: PropTypes.string,
21
- highlightChoices: PropTypes.bool,
22
- animationsDisabled: PropTypes.bool,
23
- maxNoOfSelections: PropTypes.number,
24
- };
25
-
26
- change = (tokens) => {
27
- const { onChange } = this.props;
28
-
29
- if (!onChange) {
30
- return;
31
- }
32
- const out = tokens.filter((t) => t.selected).map((t) => ({ start: t.start, end: t.end }));
33
-
34
- onChange(out);
35
- };
36
-
37
- render() {
38
- const {
39
- text,
40
- disabled,
41
- tokens,
42
- selectedTokens,
43
- className,
44
- highlightChoices,
45
- maxNoOfSelections,
46
- animationsDisabled,
47
- } = this.props;
48
-
49
- const normalized = normalize(text, tokens);
50
- log('normalized: ', normalized);
51
- const prepped = normalized.map((t) => {
52
- const selectedIndex = selectedTokens.findIndex((s) => {
53
- return s.start === t.start && s.end === t.end;
54
- });
55
- const selected = selectedIndex !== -1;
56
- const correct = selected ? t.correct : undefined;
57
- const isMissing = t.isMissing;
58
- return {
59
- ...t,
60
- selectable: !disabled && t.predefined,
61
- selected,
62
- correct,
63
- isMissing,
64
- };
65
- });
66
-
67
- return (
68
- <TokenSelect
69
- highlightChoices={!disabled && highlightChoices}
70
- className={className}
71
- tokens={prepped}
72
- disabled={disabled}
73
- onChange={this.change}
74
- maxNoOfSelections={maxNoOfSelections}
75
- animationsDisabled={animationsDisabled}
76
- />
77
- );
78
- }
79
- }