@pie-lib/text-select 1.12.8-next.1 → 1.12.8-next.1639

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 (35) hide show
  1. package/CHANGELOG.json +1 -632
  2. package/CHANGELOG.md +178 -28
  3. package/NEXT.CHANGELOG.json +1 -0
  4. package/lib/legend.js +63 -36
  5. package/lib/legend.js.map +1 -1
  6. package/lib/token-select/index.js +3 -2
  7. package/lib/token-select/index.js.map +1 -1
  8. package/lib/token-select/token.js +113 -74
  9. package/lib/token-select/token.js.map +1 -1
  10. package/lib/tokenizer/controls.js +14 -0
  11. package/lib/tokenizer/controls.js.map +1 -1
  12. package/lib/tokenizer/token-text.js +11 -2
  13. package/lib/tokenizer/token-text.js.map +1 -1
  14. package/package.json +8 -6
  15. package/src/__tests__/__snapshots__/text-select.test.jsx.snap +21 -0
  16. package/src/__tests__/text-select.test.jsx +34 -0
  17. package/src/__tests__/utils.test.jsx +27 -0
  18. package/src/legend.js +54 -36
  19. package/src/token-select/__tests__/__snapshots__/index.test.jsx.snap +49 -0
  20. package/src/token-select/__tests__/__snapshots__/token.test.jsx.snap +27 -0
  21. package/src/token-select/__tests__/index.test.jsx +257 -0
  22. package/src/token-select/__tests__/token.test.jsx +33 -0
  23. package/src/token-select/index.jsx +3 -1
  24. package/src/token-select/token.jsx +98 -72
  25. package/src/tokenizer/__tests__/__snapshots__/controls.test.jsx.snap +59 -0
  26. package/src/tokenizer/__tests__/__snapshots__/index.test.jsx.snap +31 -0
  27. package/src/tokenizer/__tests__/__snapshots__/token-text.test.jsx.snap +17 -0
  28. package/src/tokenizer/__tests__/builder.test.js +256 -0
  29. package/src/tokenizer/__tests__/controls.test.jsx +25 -0
  30. package/src/tokenizer/__tests__/index.test.jsx +140 -0
  31. package/src/tokenizer/__tests__/selection-utils.test.js +26 -0
  32. package/src/tokenizer/__tests__/token-text.test.jsx +136 -0
  33. package/src/tokenizer/controls.jsx +20 -1
  34. package/src/tokenizer/token-text.jsx +9 -0
  35. package/README.md +0 -3
@@ -0,0 +1,140 @@
1
+ import { Tokenizer } from '../index';
2
+ import React from 'react';
3
+ import { shallow } from 'enzyme';
4
+ import { words, sentences, paragraphs } from '../builder';
5
+
6
+ const tokens = () => [
7
+ {
8
+ start: 0,
9
+ end: 1,
10
+ text: 'f',
11
+ },
12
+ ];
13
+
14
+ const eff = () => tokens()[0];
15
+
16
+ jest.mock('../builder', () => ({
17
+ words: jest.fn().mockReturnValue([{ start: 0, end: 3, text: 'foo' }]),
18
+ sentences: jest.fn().mockReturnValue([{ start: 0, end: 3, text: 'foo' }]),
19
+ paragraphs: jest.fn().mockReturnValue([{ start: 0, end: 3, text: 'foo' }]),
20
+ }));
21
+ describe('tokenizer', () => {
22
+ describe('snapshot', () => {
23
+ it('renders', () => {
24
+ const w = shallow(<Tokenizer text="foo" classes={{}} onChange={jest.fn()} tokens={tokens()} />);
25
+ expect(w).toMatchSnapshot();
26
+ });
27
+ });
28
+
29
+ describe('logic', () => {
30
+ let w;
31
+ let onChange;
32
+
33
+ beforeEach(() => {
34
+ onChange = jest.fn();
35
+ w = shallow(<Tokenizer text="foo" classes={{}} onChange={onChange} tokens={tokens()} />);
36
+ });
37
+ describe('tokenIndex', () => {
38
+ it('returns 0', () => {
39
+ const index = w.instance().tokenIndex(eff());
40
+ expect(index).toEqual(0);
41
+ });
42
+
43
+ it('returns -1', () => {
44
+ const index = w.instance().tokenIndex({ start: 2, end: 3, text: 'f' });
45
+ expect(index).toEqual(-1);
46
+ });
47
+ });
48
+
49
+ describe('tokenClick', () => {
50
+ let i;
51
+
52
+ beforeEach(() => {
53
+ i = w.instance();
54
+ i.setCorrect = jest.fn();
55
+ i.removeToken = jest.fn();
56
+ });
57
+
58
+ it('calls removeToken if setCorrectMode == false', () => {
59
+ i.tokenClick(eff());
60
+ expect(i.setCorrect).not.toBeCalled();
61
+ expect(i.removeToken).toBeCalled();
62
+ });
63
+
64
+ it('calls setCorrect if setCorrectMode == true', () => {
65
+ i.setState({ setCorrectMode: true });
66
+ i.tokenClick(eff());
67
+ expect(i.setCorrect).toBeCalled();
68
+ expect(i.removeToken).not.toBeCalled();
69
+ });
70
+ });
71
+
72
+ describe('selectToken', () => {
73
+ it('calls onChange', () => {
74
+ w.instance().selectToken({ start: 1, end: 3, text: 'oo' }, [{ start: 0, end: 1, text: 'f' }]);
75
+ expect(onChange).toBeCalledWith([{ start: 1, end: 3, text: 'oo' }], '');
76
+ });
77
+ });
78
+
79
+ describe('buildParagraphsTokens', () => {
80
+ it('calls paragraphs', () => {
81
+ w.instance().buildTokens('paragraph', paragraphs);
82
+ expect(paragraphs).toBeCalledWith('foo');
83
+ });
84
+ });
85
+
86
+ describe('buildSentenceTokens', () => {
87
+ it('calls sentences', () => {
88
+ w.instance().buildTokens('sentence', sentences);
89
+ expect(sentences).toBeCalledWith('foo');
90
+ });
91
+ });
92
+
93
+ describe('buildWordTokens', () => {
94
+ it('calls words', () => {
95
+ w.instance().buildTokens('word', words);
96
+ expect(words).toBeCalledWith('foo');
97
+ });
98
+ });
99
+
100
+ describe('clear', () => {
101
+ it('calls onChange with an empty array', () => {
102
+ w.instance().clear();
103
+ expect(onChange).toBeCalledWith([], '');
104
+ });
105
+ });
106
+
107
+ describe('toggleCorrectMode', () => {
108
+ it('set state', () => {
109
+ w.setState({ setCorrectMode: true });
110
+ w.instance().toggleCorrectMode();
111
+ expect(w.state('setCorrectMode')).toEqual(false);
112
+ });
113
+ });
114
+
115
+ describe('setCorrect', () => {
116
+ it('calls onChange', () => {
117
+ w.instance().setCorrect({ start: 0, end: 1, text: 'f' });
118
+ expect(onChange).toBeCalledWith([{ start: 0, end: 1, text: 'f', correct: true }], '');
119
+ });
120
+ it('calls onChange w/ correct: false', () => {
121
+ w.setProps({
122
+ tokens: [{ start: 0, end: 1, text: 'f', correct: true }],
123
+ });
124
+ w.instance().setCorrect({ start: 0, end: 1, text: 'f' });
125
+ expect(onChange).toBeCalledWith([{ start: 0, end: 1, text: 'f', correct: false }], '');
126
+ });
127
+ });
128
+
129
+ describe('removeToken', () => {
130
+ it('calls onChange', () => {
131
+ w.instance().removeToken({ start: 0, end: 1, text: 'f' });
132
+ expect(onChange).toBeCalledWith([], '');
133
+ });
134
+ it('does not call onChange if it cant find the token', () => {
135
+ w.instance().removeToken({ start: 2, end: 3, text: 'a' });
136
+ expect(onChange).not.toBeCalled();
137
+ });
138
+ });
139
+ });
140
+ });
@@ -0,0 +1,26 @@
1
+ import { clearSelection, getCaretCharacterOffsetWithin } from '../selection-utils';
2
+
3
+ describe('selection-utils', () => {
4
+ let selection;
5
+ let range;
6
+ beforeEach(() => {
7
+ selection = {
8
+ removeAllRanges: jest.fn(),
9
+ addRange: jest.fn(),
10
+ getRangeAt: jest.fn().mockReturnValue(range),
11
+ };
12
+ global.document.getSelection = jest.fn().mockReturnValue(selection);
13
+ global.document.createRange = jest.fn();
14
+ });
15
+
16
+ describe('clearSelection', () => {
17
+ it('calls removeAllRanges', () => {
18
+ clearSelection();
19
+ expect(selection.removeAllRanges).toBeCalled();
20
+ });
21
+ });
22
+
23
+ xdescribe('getCaretCharacterOffsetWithin', () => {
24
+ it('TODO', () => {});
25
+ });
26
+ });
@@ -0,0 +1,136 @@
1
+ import { shallow } from 'enzyme';
2
+ import React from 'react';
3
+ import TokenText from '../token-text';
4
+ import { intersection } from '../builder';
5
+ import { clearSelection, getCaretCharacterOffsetWithin } from '../selection-utils';
6
+
7
+ jest.mock('../selection-utils', () => ({
8
+ clearSelection: jest.fn(),
9
+ getCaretCharacterOffsetWithin: jest.fn().mockReturnValue(10),
10
+ }));
11
+
12
+ jest.mock('../builder', () => ({
13
+ intersection: jest.fn().mockReturnValue({
14
+ hasOverlap: jest.fn().mockReturnValue(false),
15
+ surroundedTokens: jest.fn().mockReturnValue([]),
16
+ }),
17
+ normalize: jest.fn().mockReturnValue([
18
+ {
19
+ text: `lorem\nfoo bar`,
20
+ start: 0,
21
+ end: 13,
22
+ predefined: true,
23
+ },
24
+ ]),
25
+ }));
26
+
27
+ const tokens = () => [
28
+ {
29
+ start: 0,
30
+ end: 7,
31
+ text: `lorem\nfoo bar`,
32
+ },
33
+ ];
34
+
35
+ const mkEvent = () => ({
36
+ preventDefault: jest.fn(),
37
+ });
38
+
39
+ describe('token-text', () => {
40
+ describe('snapshot', () => {
41
+ it('renders', () => {
42
+ const w = shallow(
43
+ <TokenText onTokenClick={jest.fn()} onSelectToken={jest.fn()} text={`lorem\nfoo bar`} tokens={tokens()} />,
44
+ );
45
+ expect(w).toMatchSnapshot();
46
+ });
47
+ });
48
+
49
+ describe('logic', () => {
50
+ let w;
51
+ let onSelectToken;
52
+ let onTokenClick;
53
+ beforeEach(() => {
54
+ global.window.getSelection = jest.fn().mockReturnValue({
55
+ toString: () => 'bar',
56
+ });
57
+
58
+ onSelectToken = jest.fn();
59
+ onTokenClick = jest.fn();
60
+
61
+ w = shallow(
62
+ <TokenText
63
+ onTokenClick={onTokenClick}
64
+ onSelectToken={onSelectToken}
65
+ text={`lorem\nfoo bar`}
66
+ tokens={tokens()}
67
+ />,
68
+ );
69
+ });
70
+
71
+ describe('mouseup', () => {
72
+ it('calls event.preventDefault', () => {
73
+ const event = mkEvent();
74
+ w.instance().onClick(event);
75
+ expect(event.preventDefault).toBeCalled();
76
+ });
77
+
78
+ it('calls getCaretCharacterOffsetWithin', () => {
79
+ const event = mkEvent();
80
+ w.instance().root = {};
81
+ w.instance().onClick(event);
82
+ expect(getCaretCharacterOffsetWithin).toBeCalledWith({});
83
+ });
84
+
85
+ it('calls clear selection if there is an overlap', () => {
86
+ intersection.mockReturnValue({
87
+ hasOverlap: true,
88
+ });
89
+ const event = mkEvent();
90
+ w.instance().root = {};
91
+ w.instance().onClick(event);
92
+ expect(clearSelection).toBeCalled();
93
+ expect(onSelectToken).not.toBeCalled();
94
+ });
95
+
96
+ it('calls onSelectToken', () => {
97
+ const event = mkEvent();
98
+ intersection.mockReturnValue({
99
+ hasOverlap: false,
100
+ surroundedTokens: [],
101
+ });
102
+ w.instance().root = {};
103
+ w.instance().onClick(event);
104
+ expect(onSelectToken).toBeCalledWith({ text: 'bar', start: 10, end: 13 }, []);
105
+ });
106
+
107
+ it('does not call onSelectToken for ["\n", " ", "\t"]', () => {
108
+ const event = mkEvent();
109
+
110
+ intersection.mockReturnValue({
111
+ hasOverlap: false,
112
+ surroundedTokens: [],
113
+ });
114
+ w.instance().root = {};
115
+
116
+ global.window.getSelection = jest.fn().mockReturnValue({
117
+ toString: () => '\n',
118
+ });
119
+ w.instance().onClick(event);
120
+ expect(onSelectToken).not.toBeCalled();
121
+
122
+ global.window.getSelection = jest.fn().mockReturnValue({
123
+ toString: () => ' ',
124
+ });
125
+ w.instance().onClick(event);
126
+ expect(onSelectToken).not.toBeCalled();
127
+
128
+ global.window.getSelection = jest.fn().mockReturnValue({
129
+ toString: () => '\t',
130
+ });
131
+ w.instance().onClick(event);
132
+ expect(onSelectToken).not.toBeCalled();
133
+ });
134
+ });
135
+ });
136
+ });
@@ -4,6 +4,8 @@ import Button from '@material-ui/core/Button';
4
4
  import { withStyles } from '@material-ui/core/styles';
5
5
  import Switch from '@material-ui/core/Switch';
6
6
  import FormControlLabel from '@material-ui/core/FormControlLabel';
7
+ import { color } from '@pie-lib/render-ui';
8
+ import classNames from 'classnames';
7
9
 
8
10
  export class Controls extends React.Component {
9
11
  static propTypes = {
@@ -50,7 +52,18 @@ export class Controls extends React.Component {
50
52
  </Button>
51
53
  </div>
52
54
  <FormControlLabel
53
- control={<Switch checked={setCorrectMode} onChange={onToggleCorrectMode} />}
55
+ control={
56
+ <Switch
57
+ classes={{
58
+ checked: classes.checkedThumb,
59
+ bar: classNames({
60
+ [classes.checkedBar]: setCorrectMode,
61
+ }),
62
+ }}
63
+ checked={setCorrectMode}
64
+ onChange={onToggleCorrectMode}
65
+ />
66
+ }
54
67
  label="Set correct answers"
55
68
  />
56
69
  </div>
@@ -66,4 +79,10 @@ export default withStyles((theme) => ({
66
79
  alignItems: 'center',
67
80
  justifyContent: 'space-between',
68
81
  },
82
+ checkedThumb: {
83
+ color: `${color.tertiary()} !important`,
84
+ },
85
+ checkedBar: {
86
+ backgroundColor: `${color.tertiaryLight()} !important`,
87
+ },
69
88
  }))(Controls);
@@ -16,9 +16,18 @@ export const Text = withStyles(() => ({
16
16
  cursor: 'pointer',
17
17
  backgroundColor: yellow[100],
18
18
  border: `dashed 0px ${yellow[700]}`,
19
+ // we need this for nested tokenized elements like paragraphs, where p is inside span
20
+ '& *': {
21
+ cursor: 'pointer',
22
+ backgroundColor: yellow[100],
23
+ border: `dashed 0px ${yellow[700]}`,
24
+ },
19
25
  },
20
26
  correct: {
21
27
  backgroundColor: green[500],
28
+ '& *': {
29
+ backgroundColor: green[500],
30
+ },
22
31
  },
23
32
  }))(({ text, predefined, classes, onClick, correct }) => {
24
33
  const formattedText = (text || '').replace(/\n/g, '<br>');
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # text-select
2
-
3
- Some components for use with text selection.