@pie-lib/text-select 2.2.0-next.8 → 3.0.0

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/CHANGELOG.md CHANGED
@@ -3,6 +3,16 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [2.2.0-next.10](https://github.com/pie-framework/pie-lib/compare/@pie-lib/text-select@2.2.0-next.9...@pie-lib/text-select@2.2.0-next.10) (2026-04-23)
7
+
8
+ **Note:** Version bump only for package @pie-lib/text-select
9
+
10
+ # [2.2.0-next.9](https://github.com/pie-framework/pie-lib/compare/@pie-lib/text-select@2.2.0-next.8...@pie-lib/text-select@2.2.0-next.9) (2026-04-16)
11
+
12
+ ### Bug Fixes
13
+
14
+ - **text-select:** adjust markup rendering PIE-25 PIE-13 ([1d4d91d](https://github.com/pie-framework/pie-lib/commit/1d4d91d09efdaf861e384beda7d6d1e512a2281c))
15
+
6
16
  # [2.2.0-next.8](https://github.com/pie-framework/pie-lib/compare/@pie-lib/text-select@2.2.0-next.7...@pie-lib/text-select@2.2.0-next.8) (2026-04-15)
7
17
 
8
18
  **Note:** Version bump only for package @pie-lib/text-select
@@ -14,6 +14,7 @@ var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/ge
14
14
  var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
15
15
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
16
16
  var _react = _interopRequireDefault(require("react"));
17
+ var _server = require("react-dom/server");
17
18
  var _propTypes = _interopRequireDefault(require("prop-types"));
18
19
  var _token = _interopRequireWildcard(require("./token"));
19
20
  var _styles = require("@mui/material/styles");
@@ -32,18 +33,29 @@ var StyledTokenSelect = (0, _styles.styled)('div')(function () {
32
33
  whiteSpace: 'pre'
33
34
  }, (0, _styleUtils.noSelect)()), {}, {
34
35
  '& p': {
35
- whiteSpace: 'break-spaces',
36
- margin: 0
36
+ whiteSpace: 'break-spaces'
37
37
  }
38
38
  });
39
39
  });
40
40
 
41
- // strip HTML tags for plain text rendering
42
- var stripHtmlTags = function stripHtmlTags(text) {
43
- if (!text) {
44
- return text;
45
- }
46
- return text.replace(/<[^>]+>/g, '');
41
+ // Invisible container whose only job is to make Emotion inject CSS for all Token variants.
42
+ // renderToString produces correct class names but never triggers Emotion's DOM-side injection,
43
+ // so without this the class names exist in the HTML but have no matching CSS rules.
44
+ var HiddenCssPrimer = (0, _styles.styled)('div')(function () {
45
+ return {
46
+ display: 'none',
47
+ position: 'absolute',
48
+ visibility: 'hidden',
49
+ pointerEvents: 'none'
50
+ };
51
+ });
52
+ var normalizeCommonEntities = function normalizeCommonEntities() {
53
+ var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
54
+ return text.replace(/&nbsp;/gi, "\xA0");
55
+ };
56
+ var normalizeSelectableText = function normalizeSelectableText() {
57
+ var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
58
+ return normalizeCommonEntities(text).replace(/<\/p>\s*<p[^>]*>/gi, '\n\n').replace(/<br\s*\/?>/gi, '\n').replace(/<\/?(table|tbody|tr|td|p)[^>]*>/gi, '');
47
59
  };
48
60
  var TokenSelect = exports.TokenSelect = /*#__PURE__*/function (_React$Component) {
49
61
  function TokenSelect() {
@@ -113,65 +125,42 @@ var TokenSelect = exports.TokenSelect = /*#__PURE__*/function (_React$Component)
113
125
  }
114
126
  }
115
127
  });
116
- /** Build a React tree instead of an HTML string so Emotion can inject CSS */
117
- (0, _defineProperty2["default"])(_this, "generateTokensNodes", function () {
128
+ /**
129
+ * Build an HTML string so that non-selectable token text (which may contain arbitrary or even
130
+ * *partial* HTML — e.g. just an opening <table><tbody><tr><td> in one token and the matching
131
+ * closing tags in another) is preserved exactly as-is. Selectable Token components are
132
+ * serialised via renderToString; their Emotion class names are stable hashes so they match the
133
+ * CSS that the HiddenCssPrimer forces Emotion to inject into the document.
134
+ */
135
+ (0, _defineProperty2["default"])(_this, "generateTokensInHtml", function () {
118
136
  var _this$props3 = _this.props,
119
137
  tokens = _this$props3.tokens,
120
138
  disabled = _this$props3.disabled,
121
139
  highlightChoices = _this$props3.highlightChoices,
122
140
  animationsDisabled = _this$props3.animationsDisabled;
123
141
  var selectedCount = _this.selectedCount();
124
- var isLineBreak = function isLineBreak(text) {
125
- return text === '\n';
126
- };
127
- var isNewParagraph = function isNewParagraph(text) {
128
- return text === '\n\n';
129
- };
130
- var paragraphs = [];
131
- var currentChildren = [];
132
- var flushParagraph = function flushParagraph() {
133
- // Always push a <p>, even if empty, to mirror previous behavior
134
- paragraphs.push(/*#__PURE__*/_react["default"].createElement("p", {
135
- key: "p-".concat(paragraphs.length)
136
- }, currentChildren));
137
- currentChildren = [];
138
- };
139
- (tokens || []).forEach(function (t, index) {
142
+ var reducer = function reducer(accumulator, t, index) {
140
143
  var selectable = t.selected || t.selectable && _this.canSelectMore(selectedCount);
141
144
  var showCorrectAnswer = t.correct !== undefined && (t.selectable || t.selected);
142
- if (isNewParagraph(t.text)) {
143
- flushParagraph();
144
- return;
145
- }
146
- if (isLineBreak(t.text)) {
147
- currentChildren.push(/*#__PURE__*/_react["default"].createElement("br", {
148
- key: "br-".concat(index)
149
- }));
150
- return;
151
- }
152
- if (selectable && !disabled || showCorrectAnswer || t.selected || t.isMissing || animationsDisabled && t.predefined // print mode
153
- ) {
154
- currentChildren.push(/*#__PURE__*/_react["default"].createElement(_token["default"], (0, _extends2["default"])({
145
+ if (t.text === '\n\n') return "".concat(accumulator, "</p><p>");
146
+ if (t.text === '\n') return "".concat(accumulator, "<br>");
147
+ if (selectable && !disabled || showCorrectAnswer || t.selected || t.isMissing || animationsDisabled && t.predefined) {
148
+ return accumulator + (0, _server.renderToString)(/*#__PURE__*/_react["default"].createElement(_token["default"], (0, _extends2["default"])({
155
149
  key: index,
156
150
  disabled: disabled,
157
151
  index: index
158
152
  }, t, {
159
- text: stripHtmlTags(t.text),
153
+ text: normalizeSelectableText(t.text),
160
154
  selectable: selectable,
161
155
  highlight: highlightChoices,
162
156
  animationsDisabled: animationsDisabled
163
157
  })));
164
- } else {
165
- // raw text node – React will escape as needed
166
- currentChildren.push(/*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, {
167
- key: index
168
- }, stripHtmlTags(t.text)));
169
158
  }
170
- });
171
159
 
172
- // flush last paragraph
173
- flushParagraph();
174
- return paragraphs;
160
+ // Non-selectable: emit raw HTML unchanged (may contain partial tags, tables, lists, etc.)
161
+ return accumulator + normalizeCommonEntities(t.text);
162
+ };
163
+ return (tokens || []).reduce(reducer, '<p>') + '</p>';
175
164
  });
176
165
  return _this;
177
166
  }
@@ -180,11 +169,83 @@ var TokenSelect = exports.TokenSelect = /*#__PURE__*/function (_React$Component)
180
169
  key: "render",
181
170
  value: function render() {
182
171
  var classNameProp = this.props.className;
183
- var nodes = this.generateTokensNodes();
184
- return /*#__PURE__*/_react["default"].createElement(StyledTokenSelect, {
172
+ var html = this.generateTokensInHtml();
173
+
174
+ // Render one invisible Token per visual variant so Emotion injects all CSS rules into the
175
+ // document before the browser paints the dangerouslySetInnerHTML content.
176
+ var primerText = ' ';
177
+ return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement(HiddenCssPrimer, {
178
+ "aria-hidden": "true"
179
+ }, /*#__PURE__*/_react["default"].createElement(_token["default"], {
180
+ text: primerText,
181
+ index: -1,
182
+ selectable: true,
183
+ disabled: false,
184
+ highlight: false,
185
+ animationsDisabled: false
186
+ }), /*#__PURE__*/_react["default"].createElement(_token["default"], {
187
+ text: primerText,
188
+ index: -1,
189
+ selectable: true,
190
+ disabled: false,
191
+ highlight: true,
192
+ animationsDisabled: false
193
+ }), /*#__PURE__*/_react["default"].createElement(_token["default"], {
194
+ text: primerText,
195
+ index: -1,
196
+ selectable: true,
197
+ selected: true,
198
+ disabled: false,
199
+ highlight: false,
200
+ animationsDisabled: false
201
+ }), /*#__PURE__*/_react["default"].createElement(_token["default"], {
202
+ text: primerText,
203
+ index: -1,
204
+ selectable: true,
205
+ selected: true,
206
+ disabled: true,
207
+ highlight: false,
208
+ animationsDisabled: false
209
+ }), /*#__PURE__*/_react["default"].createElement(_token["default"], {
210
+ text: primerText,
211
+ index: -1,
212
+ selectable: true,
213
+ disabled: false,
214
+ highlight: false,
215
+ animationsDisabled: true,
216
+ predefined: true
217
+ }), /*#__PURE__*/_react["default"].createElement(_token["default"], {
218
+ text: primerText,
219
+ index: -1,
220
+ selectable: true,
221
+ selected: true,
222
+ correct: true,
223
+ disabled: false,
224
+ highlight: false,
225
+ animationsDisabled: false
226
+ }), /*#__PURE__*/_react["default"].createElement(_token["default"], {
227
+ text: primerText,
228
+ index: -1,
229
+ selectable: true,
230
+ selected: true,
231
+ correct: false,
232
+ disabled: false,
233
+ highlight: false,
234
+ animationsDisabled: false
235
+ }), /*#__PURE__*/_react["default"].createElement(_token["default"], {
236
+ text: primerText,
237
+ index: -1,
238
+ isMissing: true,
239
+ disabled: false,
240
+ highlight: false,
241
+ animationsDisabled: false
242
+ })), /*#__PURE__*/_react["default"].createElement(StyledTokenSelect, {
185
243
  className: classNameProp,
186
- onClick: this.toggleToken
187
- }, nodes);
244
+ onClick: this.toggleToken,
245
+ dangerouslySetInnerHTML: {
246
+ __html: html
247
+ }
248
+ }));
188
249
  }
189
250
  }]);
190
251
  }(_react["default"].Component);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_react","_interopRequireDefault","require","_propTypes","_token","_interopRequireWildcard","_styles","_lodashEs","_debug","_styleUtils","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","_typeof","has","get","set","_t","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_callSuper","_getPrototypeOf2","_possibleConstructorReturn2","_isNativeReflectConstruct","Reflect","construct","constructor","apply","Boolean","prototype","valueOf","ownKeys","keys","getOwnPropertySymbols","filter","enumerable","push","_objectSpread","arguments","length","forEach","_defineProperty2","getOwnPropertyDescriptors","defineProperties","log","debug","StyledTokenSelect","styled","backgroundColor","whiteSpace","noSelect","margin","stripHtmlTags","text","replace","TokenSelect","exports","_React$Component","_this","_classCallCheck2","_len","args","Array","_key","concat","props","tokens","selected","selectedCount","maxNoOfSelections","isFinite","event","_target$closest","_targetSpanWrapper$da","target","_this$props","animationsDisabled","tokensCloned","clone","targetSpanWrapper","closest","Token","rootClassName","targetedTokenIndex","dataset","indexkey","undefined","correct","isMissing","_this$props2","onChange","selectedToken","tk","updatedTokens","map","token","isEqual","selectable","update","splice","_this$props3","disabled","highlightChoices","isLineBreak","isNewParagraph","paragraphs","currentChildren","flushParagraph","createElement","key","index","canSelectMore","showCorrectAnswer","predefined","_extends2","highlight","Fragment","_inherits2","_createClass2","value","render","classNameProp","className","nodes","generateTokensNodes","onClick","toggleToken","React","Component","PropTypes","arrayOf","shape","TokenTypes","isRequired","string","func","bool","number","_default"],"sources":["../../src/token-select/index.jsx"],"sourcesContent":["import React from 'react';\nimport PropTypes from 'prop-types';\nimport Token, { TokenTypes } from './token';\nimport { styled } from '@mui/material/styles';\nimport { clone, isEqual } from 'lodash-es';\nimport debug from 'debug';\nimport { noSelect } from '@pie-lib/style-utils';\n\nconst log = debug('@pie-lib:text-select:token-select');\n\nconst StyledTokenSelect = styled('div')(() => ({\n backgroundColor: 'none',\n whiteSpace: 'pre',\n ...noSelect(),\n '& p': {\n whiteSpace: 'break-spaces',\n margin: 0,\n },\n}));\n\n// strip HTML tags for plain text rendering\nconst stripHtmlTags = (text) => {\n if (!text) {\n return text;\n }\n\n return text.replace(/<[^>]+>/g, '');\n};\n\nexport class TokenSelect extends React.Component {\n static propTypes = {\n tokens: PropTypes.arrayOf(PropTypes.shape(TokenTypes)).isRequired,\n className: PropTypes.string,\n onChange: PropTypes.func.isRequired,\n disabled: PropTypes.bool,\n highlightChoices: PropTypes.bool,\n animationsDisabled: PropTypes.bool,\n maxNoOfSelections: PropTypes.number,\n };\n\n static defaultProps = {\n highlightChoices: false,\n maxNoOfSelections: 0,\n tokens: [],\n };\n\n selectedCount = () => this.props.tokens.filter((t) => t.selected).length;\n\n canSelectMore = (selectedCount) => {\n const { maxNoOfSelections } = this.props;\n\n if (maxNoOfSelections === 1) return true;\n\n log('[canSelectMore] maxNoOfSelections: ', maxNoOfSelections, 'selectedCount: ', selectedCount);\n return maxNoOfSelections <= 0 || (isFinite(maxNoOfSelections) && selectedCount < maxNoOfSelections);\n };\n\n toggleToken = (event) => {\n const { target } = event;\n const { tokens, animationsDisabled } = this.props;\n const tokensCloned = clone(tokens);\n\n const targetSpanWrapper = target.closest?.(`.${Token.rootClassName}`);\n const targetedTokenIndex = targetSpanWrapper?.dataset?.indexkey;\n const t = targetedTokenIndex !== undefined ? tokensCloned[targetedTokenIndex] : undefined;\n\n // don't toggle if in print mode, correctness is defined, or is missing\n if (t && t.correct === undefined && !animationsDisabled && !t.isMissing) {\n const { onChange, maxNoOfSelections } = this.props;\n const selected = !t.selected;\n\n if (maxNoOfSelections === 1 && this.selectedCount() === 1) {\n const selectedToken = (tokens || []).filter((tk) => tk.selected);\n const updatedTokens = tokensCloned.map((token) => {\n if (isEqual(token, selectedToken[0])) {\n return { ...token, selected: false };\n }\n return { ...token, selectable: true };\n });\n\n const update = { ...t, selected };\n updatedTokens.splice(targetedTokenIndex, 1, update);\n onChange(updatedTokens);\n } else {\n if (selected && maxNoOfSelections > 0 && this.selectedCount() >= maxNoOfSelections) {\n log('skip toggle max reached');\n return;\n }\n const update = { ...t, selected };\n tokensCloned.splice(targetedTokenIndex, 1, update);\n onChange(tokensCloned);\n }\n }\n };\n\n /** Build a React tree instead of an HTML string so Emotion can inject CSS */\n generateTokensNodes = () => {\n const { tokens, disabled, highlightChoices, animationsDisabled } = this.props;\n const selectedCount = this.selectedCount();\n\n const isLineBreak = (text) => text === '\\n';\n const isNewParagraph = (text) => text === '\\n\\n';\n\n const paragraphs = [];\n let currentChildren = [];\n\n const flushParagraph = () => {\n // Always push a <p>, even if empty, to mirror previous behavior\n paragraphs.push(<p key={`p-${paragraphs.length}`}>{currentChildren}</p>);\n currentChildren = [];\n };\n\n (tokens || []).forEach((t, index) => {\n const selectable = t.selected || (t.selectable && this.canSelectMore(selectedCount));\n const showCorrectAnswer = t.correct !== undefined && (t.selectable || t.selected);\n\n if (isNewParagraph(t.text)) {\n flushParagraph();\n return;\n }\n\n if (isLineBreak(t.text)) {\n currentChildren.push(<br key={`br-${index}`} />);\n return;\n }\n\n if (\n (selectable && !disabled) ||\n showCorrectAnswer ||\n t.selected ||\n t.isMissing ||\n (animationsDisabled && t.predefined) // print mode\n ) {\n currentChildren.push(\n <Token\n key={index}\n disabled={disabled}\n index={index}\n {...t}\n text={stripHtmlTags(t.text)}\n selectable={selectable}\n highlight={highlightChoices}\n animationsDisabled={animationsDisabled}\n />,\n );\n } else {\n // raw text node – React will escape as needed\n currentChildren.push(<React.Fragment key={index}>{stripHtmlTags(t.text)}</React.Fragment>);\n }\n });\n\n // flush last paragraph\n flushParagraph();\n\n return paragraphs;\n };\n\n render() {\n const { className: classNameProp } = this.props;\n const nodes = this.generateTokensNodes();\n\n return (\n <StyledTokenSelect className={classNameProp} onClick={this.toggleToken}>\n {nodes}\n </StyledTokenSelect>\n );\n }\n}\n\nexport default TokenSelect;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["_react","_interopRequireDefault","require","_server","_propTypes","_token","_interopRequireWildcard","_styles","_lodashEs","_debug","_styleUtils","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","_typeof","has","get","set","_t","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_callSuper","_getPrototypeOf2","_possibleConstructorReturn2","_isNativeReflectConstruct","Reflect","construct","constructor","apply","Boolean","prototype","valueOf","ownKeys","keys","getOwnPropertySymbols","filter","enumerable","push","_objectSpread","arguments","length","forEach","_defineProperty2","getOwnPropertyDescriptors","defineProperties","log","debug","StyledTokenSelect","styled","backgroundColor","whiteSpace","noSelect","HiddenCssPrimer","display","position","visibility","pointerEvents","normalizeCommonEntities","text","undefined","replace","normalizeSelectableText","TokenSelect","exports","_React$Component","_this","_classCallCheck2","_len","args","Array","_key","concat","props","tokens","selected","selectedCount","maxNoOfSelections","isFinite","event","_target$closest","_targetSpanWrapper$da","target","_this$props","animationsDisabled","tokensCloned","clone","targetSpanWrapper","closest","Token","rootClassName","targetedTokenIndex","dataset","indexkey","correct","isMissing","_this$props2","onChange","selectedToken","tk","updatedTokens","map","token","isEqual","selectable","update","splice","_this$props3","disabled","highlightChoices","reducer","accumulator","index","canSelectMore","showCorrectAnswer","predefined","renderToString","createElement","_extends2","key","highlight","reduce","_inherits2","_createClass2","value","render","classNameProp","className","html","generateTokensInHtml","primerText","Fragment","onClick","toggleToken","dangerouslySetInnerHTML","__html","React","Component","PropTypes","arrayOf","shape","TokenTypes","isRequired","string","func","bool","number","_default"],"sources":["../../src/token-select/index.jsx"],"sourcesContent":["import React from 'react';\nimport { renderToString } from 'react-dom/server';\nimport PropTypes from 'prop-types';\nimport Token, { TokenTypes } from './token';\nimport { styled } from '@mui/material/styles';\nimport { clone, isEqual } from 'lodash-es';\nimport debug from 'debug';\nimport { noSelect } from '@pie-lib/style-utils';\n\nconst log = debug('@pie-lib:text-select:token-select');\n\nconst StyledTokenSelect = styled('div')(() => ({\n backgroundColor: 'none',\n whiteSpace: 'pre',\n ...noSelect(),\n '& p': {\n whiteSpace: 'break-spaces',\n },\n}));\n\n// Invisible container whose only job is to make Emotion inject CSS for all Token variants.\n// renderToString produces correct class names but never triggers Emotion's DOM-side injection,\n// so without this the class names exist in the HTML but have no matching CSS rules.\nconst HiddenCssPrimer = styled('div')(() => ({\n display: 'none',\n position: 'absolute',\n visibility: 'hidden',\n pointerEvents: 'none',\n}));\n\nconst normalizeCommonEntities = (text = '') => text.replace(/&nbsp;/gi, '\\u00a0');\n\nconst normalizeSelectableText = (text = '') =>\n normalizeCommonEntities(text)\n .replace(/<\\/p>\\s*<p[^>]*>/gi, '\\n\\n')\n .replace(/<br\\s*\\/?>/gi, '\\n')\n .replace(/<\\/?(table|tbody|tr|td|p)[^>]*>/gi, '');\n\nexport class TokenSelect extends React.Component {\n static propTypes = {\n tokens: PropTypes.arrayOf(PropTypes.shape(TokenTypes)).isRequired,\n className: PropTypes.string,\n onChange: PropTypes.func.isRequired,\n disabled: PropTypes.bool,\n highlightChoices: PropTypes.bool,\n animationsDisabled: PropTypes.bool,\n maxNoOfSelections: PropTypes.number,\n };\n\n static defaultProps = {\n highlightChoices: false,\n maxNoOfSelections: 0,\n tokens: [],\n };\n\n selectedCount = () => this.props.tokens.filter((t) => t.selected).length;\n\n canSelectMore = (selectedCount) => {\n const { maxNoOfSelections } = this.props;\n\n if (maxNoOfSelections === 1) return true;\n\n log('[canSelectMore] maxNoOfSelections: ', maxNoOfSelections, 'selectedCount: ', selectedCount);\n return maxNoOfSelections <= 0 || (isFinite(maxNoOfSelections) && selectedCount < maxNoOfSelections);\n };\n\n toggleToken = (event) => {\n const { target } = event;\n const { tokens, animationsDisabled } = this.props;\n const tokensCloned = clone(tokens);\n\n const targetSpanWrapper = target.closest?.(`.${Token.rootClassName}`);\n const targetedTokenIndex = targetSpanWrapper?.dataset?.indexkey;\n const t = targetedTokenIndex !== undefined ? tokensCloned[targetedTokenIndex] : undefined;\n\n // don't toggle if in print mode, correctness is defined, or is missing\n if (t && t.correct === undefined && !animationsDisabled && !t.isMissing) {\n const { onChange, maxNoOfSelections } = this.props;\n const selected = !t.selected;\n\n if (maxNoOfSelections === 1 && this.selectedCount() === 1) {\n const selectedToken = (tokens || []).filter((tk) => tk.selected);\n const updatedTokens = tokensCloned.map((token) => {\n if (isEqual(token, selectedToken[0])) {\n return { ...token, selected: false };\n }\n return { ...token, selectable: true };\n });\n\n const update = { ...t, selected };\n updatedTokens.splice(targetedTokenIndex, 1, update);\n onChange(updatedTokens);\n } else {\n if (selected && maxNoOfSelections > 0 && this.selectedCount() >= maxNoOfSelections) {\n log('skip toggle max reached');\n return;\n }\n const update = { ...t, selected };\n tokensCloned.splice(targetedTokenIndex, 1, update);\n onChange(tokensCloned);\n }\n }\n };\n\n /**\n * Build an HTML string so that non-selectable token text (which may contain arbitrary or even\n * *partial* HTML — e.g. just an opening <table><tbody><tr><td> in one token and the matching\n * closing tags in another) is preserved exactly as-is. Selectable Token components are\n * serialised via renderToString; their Emotion class names are stable hashes so they match the\n * CSS that the HiddenCssPrimer forces Emotion to inject into the document.\n */\n generateTokensInHtml = () => {\n const { tokens, disabled, highlightChoices, animationsDisabled } = this.props;\n const selectedCount = this.selectedCount();\n\n const reducer = (accumulator, t, index) => {\n const selectable = t.selected || (t.selectable && this.canSelectMore(selectedCount));\n const showCorrectAnswer = t.correct !== undefined && (t.selectable || t.selected);\n\n if (t.text === '\\n\\n') return `${accumulator}</p><p>`;\n\n if (t.text === '\\n') return `${accumulator}<br>`;\n\n if (\n (selectable && !disabled) ||\n showCorrectAnswer ||\n t.selected ||\n t.isMissing ||\n (animationsDisabled && t.predefined)\n ) {\n return (\n accumulator +\n renderToString(\n <Token\n key={index}\n disabled={disabled}\n index={index}\n {...t}\n text={normalizeSelectableText(t.text)}\n selectable={selectable}\n highlight={highlightChoices}\n animationsDisabled={animationsDisabled}\n />,\n )\n );\n }\n\n // Non-selectable: emit raw HTML unchanged (may contain partial tags, tables, lists, etc.)\n return accumulator + normalizeCommonEntities(t.text);\n };\n\n return (tokens || []).reduce(reducer, '<p>') + '</p>';\n };\n\n render() {\n const { className: classNameProp } = this.props;\n const html = this.generateTokensInHtml();\n\n // Render one invisible Token per visual variant so Emotion injects all CSS rules into the\n // document before the browser paints the dangerouslySetInnerHTML content.\n const primerText = ' ';\n return (\n <>\n <HiddenCssPrimer aria-hidden=\"true\">\n {/* base / selectable */}\n <Token\n text={primerText}\n index={-1}\n selectable\n disabled={false}\n highlight={false}\n animationsDisabled={false}\n />\n {/* highlight */}\n <Token text={primerText} index={-1} selectable disabled={false} highlight animationsDisabled={false} />\n {/* selected */}\n <Token\n text={primerText}\n index={-1}\n selectable\n selected\n disabled={false}\n highlight={false}\n animationsDisabled={false}\n />\n {/* disabled + selected */}\n <Token\n text={primerText}\n index={-1}\n selectable\n selected\n disabled\n highlight={false}\n animationsDisabled={false}\n />\n {/* print / animationsDisabled */}\n <Token\n text={primerText}\n index={-1}\n selectable\n disabled={false}\n highlight={false}\n animationsDisabled\n predefined\n />\n {/* correct */}\n <Token\n text={primerText}\n index={-1}\n selectable\n selected\n correct\n disabled={false}\n highlight={false}\n animationsDisabled={false}\n />\n {/* incorrect */}\n <Token\n text={primerText}\n index={-1}\n selectable\n selected\n correct={false}\n disabled={false}\n highlight={false}\n animationsDisabled={false}\n />\n {/* missing */}\n <Token text={primerText} index={-1} isMissing disabled={false} highlight={false} animationsDisabled={false} />\n </HiddenCssPrimer>\n\n <StyledTokenSelect\n className={classNameProp}\n onClick={this.toggleToken}\n dangerouslySetInnerHTML={{ __html: html }}\n />\n </>\n );\n }\n}\n\nexport default TokenSelect;\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "2.2.0-next.8",
6
+ "version": "3.0.0",
7
7
  "description": "Some react components for text selection",
8
8
  "keywords": [
9
9
  "react",
@@ -22,9 +22,9 @@
22
22
  "@mui/icons-material": "^7.3.4",
23
23
  "@mui/material": "^7.3.4",
24
24
  "@pie-framework/parse-english": "^1.0.0",
25
- "@pie-lib/render-ui": "^5.2.0-next.8",
26
- "@pie-lib/style-utils": "^1.2.0-next.3",
27
- "@pie-lib/translator": "^3.2.0-next.3",
25
+ "@pie-lib/render-ui": "^6.0.0",
26
+ "@pie-lib/style-utils": "^2.0.0",
27
+ "@pie-lib/translator": "^4.0.0",
28
28
  "classnames": "^2.2.6",
29
29
  "debug": "^4.1.1",
30
30
  "lodash-es": "^4.17.23",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "@pie-framework/parse-english": "^1.0.0",
35
- "@pie-lib/test-utils": "^1.2.0-next.3",
35
+ "@pie-lib/test-utils": "^2.0.0",
36
36
  "natural": "^0.6.3",
37
37
  "react": "^18.2.0",
38
38
  "react-dom": "^18.2.0"
@@ -40,6 +40,6 @@
40
40
  "peerDependencies": {
41
41
  "react": "^18.2.0"
42
42
  },
43
- "gitHead": "80a22e0d49de8a923a1130afa9532e6d2d58eadd",
43
+ "gitHead": "bf0904ba8bdd2d6f88fb8ad99060e45e40180348",
44
44
  "scripts": {}
45
45
  }
@@ -14,7 +14,6 @@ describe('token-select', () => {
14
14
  selected: false,
15
15
  },
16
16
  ],
17
- classes: {},
18
17
  onChange: jest.fn(),
19
18
  };
20
19
 
@@ -134,12 +133,18 @@ describe('token-select', () => {
134
133
  ];
135
134
  const { container } = render(<TokenSelect {...defaultProps} tokens={tokens} />);
136
135
  expect(container.textContent).toContain('bold text');
137
- expect(container.innerHTML).not.toContain('<b>');
136
+ // The selectable token should have HTML tags stripped before rendering
137
+ // Look specifically in the main content div (not the hidden primer)
138
+ const mainDiv = container.querySelectorAll('div[class*="css-"]')[1];
139
+ expect(mainDiv.textContent).toContain('bold text');
138
140
  });
139
141
 
140
142
  it('renders with custom className', () => {
141
143
  const { container } = render(<TokenSelect {...defaultProps} className="custom-token-select" />);
142
- expect(container.firstChild).toHaveClass('custom-token-select');
144
+ // The second div with Emotion class should have the custom className
145
+ const styledDivs = container.querySelectorAll('div[class*="css-"]');
146
+ const mainDiv = styledDivs[styledDivs.length - 1];
147
+ expect(mainDiv).toHaveClass('custom-token-select');
143
148
  });
144
149
 
145
150
  it('renders tokens with correct and incorrect states', () => {
@@ -205,7 +210,15 @@ describe('token-select', () => {
205
210
  ];
206
211
  const { container } = render(<TokenSelect {...defaultProps} tokens={tokens} onChange={onChange} />);
207
212
 
208
- const tokenElement = container.querySelector('.tokenRootClass');
213
+ const tokenElements = container.querySelectorAll('.tokenRootClass');
214
+ // Find the first token with data-indexkey >= 0 (skip primer tokens at -1)
215
+ let tokenElement = null;
216
+ for (const el of tokenElements) {
217
+ if (parseInt(el.getAttribute('data-indexkey'), 10) >= 0) {
218
+ tokenElement = el;
219
+ break;
220
+ }
221
+ }
209
222
  if (tokenElement) {
210
223
  fireEvent.click(tokenElement);
211
224
  expect(onChange).toHaveBeenCalled();
@@ -239,8 +252,12 @@ describe('token-select', () => {
239
252
  );
240
253
 
241
254
  const tokenElements = container.querySelectorAll('.tokenRootClass');
242
- if (tokenElements.length > 1) {
243
- fireEvent.click(tokenElements[1]);
255
+ // Find tokens with data-indexkey >= 0 (skip primer tokens)
256
+ const realTokens = Array.from(tokenElements).filter(
257
+ (el) => parseInt(el.getAttribute('data-indexkey'), 10) >= 0,
258
+ );
259
+ if (realTokens.length > 1) {
260
+ fireEvent.click(realTokens[1]);
244
261
  expect(onChange).toHaveBeenCalled();
245
262
  const updatedTokens = onChange.mock.calls[0][0];
246
263
  expect(updatedTokens[0].selected).toBe(false);
@@ -281,8 +298,11 @@ describe('token-select', () => {
281
298
  );
282
299
 
283
300
  const tokenElements = container.querySelectorAll('.tokenRootClass');
284
- if (tokenElements.length > 2) {
285
- fireEvent.click(tokenElements[2]);
301
+ const realTokens = Array.from(tokenElements).filter(
302
+ (el) => parseInt(el.getAttribute('data-indexkey'), 10) >= 0,
303
+ );
304
+ if (realTokens.length > 2) {
305
+ fireEvent.click(realTokens[2]);
286
306
  // onChange should not be called because max is reached
287
307
  expect(onChange).not.toHaveBeenCalled();
288
308
  }
@@ -369,7 +389,14 @@ describe('token-select', () => {
369
389
  ];
370
390
  const { container } = render(<TokenSelect {...defaultProps} tokens={tokens} onChange={onChange} />);
371
391
 
372
- const tokenElement = container.querySelector('.tokenRootClass');
392
+ const tokenElements = container.querySelectorAll('.tokenRootClass');
393
+ let tokenElement = null;
394
+ for (const el of tokenElements) {
395
+ if (parseInt(el.getAttribute('data-indexkey'), 10) >= 0) {
396
+ tokenElement = el;
397
+ break;
398
+ }
399
+ }
373
400
  if (tokenElement) {
374
401
  fireEvent.click(tokenElement);
375
402
  expect(onChange).toHaveBeenCalled();
@@ -379,6 +406,215 @@ describe('token-select', () => {
379
406
  });
380
407
  });
381
408
 
409
+ describe('CSS injection and styling', () => {
410
+ it('renders HiddenCssPrimer to inject CSS for all Token variants', () => {
411
+ const { container } = render(<TokenSelect {...defaultProps} />);
412
+ // Check that the component renders without errors; the HiddenCssPrimer
413
+ // ensures CSS is injected into the document for all token states.
414
+ expect(container.firstChild).toBeInTheDocument();
415
+ // The HiddenCssPrimer should have aria-hidden to mark it as invisible
416
+ const primer = container.querySelector('[aria-hidden="true"]');
417
+ expect(primer).toBeInTheDocument();
418
+ });
419
+
420
+ it('renders tokens with Emotion CSS class names', () => {
421
+ const { container } = render(<TokenSelect {...defaultProps} />);
422
+ const tokenElement = container.querySelector('.tokenRootClass');
423
+ expect(tokenElement).toBeInTheDocument();
424
+ // Should have Emotion-generated class name like css-xxxxx
425
+ const hasEmotionClass = Array.from(tokenElement.classList).some((cls) => cls.startsWith('css-'));
426
+ expect(hasEmotionClass).toBe(true);
427
+ });
428
+ });
429
+
430
+ describe('HTML preservation', () => {
431
+ it('preserves non-selectable HTML content', () => {
432
+ const tokens = [
433
+ {
434
+ text: '<table><tr><td>table content</td></tr></table>',
435
+ start: 0,
436
+ end: 46,
437
+ predefined: false,
438
+ selectable: false,
439
+ selected: false,
440
+ },
441
+ ];
442
+ const { container } = render(<TokenSelect {...defaultProps} tokens={tokens} />);
443
+ // Non-selectable HTML should be rendered as-is
444
+ const html = container.innerHTML;
445
+ expect(html).toContain('table');
446
+ expect(html).toContain('table content');
447
+ });
448
+
449
+ it('preserves non-breaking spaces in HTML', () => {
450
+ const tokens = [
451
+ {
452
+ text: 'word&nbsp;space',
453
+ start: 0,
454
+ end: 14,
455
+ predefined: false,
456
+ selectable: false,
457
+ selected: false,
458
+ },
459
+ ];
460
+ const { container } = render(<TokenSelect {...defaultProps} tokens={tokens} />);
461
+ // &nbsp; should be converted to non-breaking space character
462
+ expect(container.textContent).toContain('word');
463
+ expect(container.textContent).toContain('space');
464
+ });
465
+
466
+ it('handles mixed selectable and non-selectable tokens', () => {
467
+ const tokens = [
468
+ {
469
+ text: 'prefix ',
470
+ start: 0,
471
+ end: 7,
472
+ predefined: false,
473
+ selectable: false,
474
+ selected: false,
475
+ },
476
+ {
477
+ text: 'selectable token',
478
+ start: 7,
479
+ end: 22,
480
+ predefined: true,
481
+ selectable: true,
482
+ selected: false,
483
+ },
484
+ {
485
+ text: ' suffix',
486
+ start: 22,
487
+ end: 29,
488
+ predefined: false,
489
+ selectable: false,
490
+ selected: false,
491
+ },
492
+ ];
493
+ const { container } = render(<TokenSelect {...defaultProps} tokens={tokens} />);
494
+ expect(container.textContent).toContain('prefix');
495
+ expect(container.textContent).toContain('selectable token');
496
+ expect(container.textContent).toContain('suffix');
497
+ });
498
+ });
499
+
500
+ describe('newline handling', () => {
501
+ it('renders single newlines as <br> tags', () => {
502
+ const tokens = [
503
+ {
504
+ text: 'line1',
505
+ start: 0,
506
+ end: 5,
507
+ predefined: true,
508
+ selectable: true,
509
+ selected: false,
510
+ },
511
+ {
512
+ text: '\n',
513
+ start: 5,
514
+ end: 6,
515
+ selected: false,
516
+ },
517
+ {
518
+ text: 'line2',
519
+ start: 6,
520
+ end: 11,
521
+ predefined: true,
522
+ selectable: true,
523
+ selected: false,
524
+ },
525
+ ];
526
+ const { container } = render(<TokenSelect {...defaultProps} tokens={tokens} />);
527
+ const html = container.innerHTML;
528
+ expect(html).toContain('<br>');
529
+ });
530
+
531
+ it('renders double newlines as paragraph breaks', () => {
532
+ const tokens = [
533
+ {
534
+ text: 'paragraph1',
535
+ start: 0,
536
+ end: 10,
537
+ predefined: true,
538
+ selectable: true,
539
+ selected: false,
540
+ },
541
+ {
542
+ text: '\n\n',
543
+ start: 10,
544
+ end: 12,
545
+ selected: false,
546
+ },
547
+ {
548
+ text: 'paragraph2',
549
+ start: 12,
550
+ end: 22,
551
+ predefined: true,
552
+ selectable: true,
553
+ selected: false,
554
+ },
555
+ ];
556
+ const { container } = render(<TokenSelect {...defaultProps} tokens={tokens} />);
557
+ const html = container.innerHTML;
558
+ expect(html).toContain('</p>');
559
+ expect(html).toContain('<p>');
560
+ });
561
+ });
562
+
563
+ describe('edge cases', () => {
564
+ it('handles empty token list', () => {
565
+ const { container } = render(<TokenSelect {...defaultProps} tokens={[]} />);
566
+ expect(container.firstChild).toBeInTheDocument();
567
+ });
568
+
569
+ it('handles tokens with special characters', () => {
570
+ const tokens = [
571
+ {
572
+ text: 'special & < > " chars',
573
+ start: 0,
574
+ end: 21,
575
+ predefined: true,
576
+ selectable: true,
577
+ selected: false,
578
+ },
579
+ ];
580
+ const { container } = render(<TokenSelect {...defaultProps} tokens={tokens} />);
581
+ expect(container.textContent).toContain('special');
582
+ });
583
+
584
+ it('renders with disabled and highlightChoices together', () => {
585
+ const tokens = [
586
+ {
587
+ text: 'token',
588
+ start: 0,
589
+ end: 5,
590
+ predefined: true,
591
+ selectable: true,
592
+ selected: false,
593
+ },
594
+ ];
595
+ const { container } = render(
596
+ <TokenSelect {...defaultProps} tokens={tokens} disabled highlightChoices />,
597
+ );
598
+ expect(container.firstChild).toBeInTheDocument();
599
+ });
600
+
601
+ it('handles very long token text', () => {
602
+ const longText = 'Lorem ipsum '.repeat(100);
603
+ const tokens = [
604
+ {
605
+ text: longText,
606
+ start: 0,
607
+ end: longText.length,
608
+ predefined: true,
609
+ selectable: true,
610
+ selected: false,
611
+ },
612
+ ];
613
+ const { container } = render(<TokenSelect {...defaultProps} tokens={tokens} />);
614
+ expect(container.textContent).toContain('Lorem ipsum');
615
+ });
616
+ });
617
+
382
618
  // Note: Tests for internal methods (selectedCount, canSelectMore, toggleToken) are
383
619
  // implementation details and cannot be directly tested with RTL. The original tests
384
620
  // used wrapper.instance() to test these methods, which tests implementation rather
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { renderToString } from 'react-dom/server';
2
3
  import PropTypes from 'prop-types';
3
4
  import Token, { TokenTypes } from './token';
4
5
  import { styled } from '@mui/material/styles';
@@ -14,18 +15,26 @@ const StyledTokenSelect = styled('div')(() => ({
14
15
  ...noSelect(),
15
16
  '& p': {
16
17
  whiteSpace: 'break-spaces',
17
- margin: 0,
18
18
  },
19
19
  }));
20
20
 
21
- // strip HTML tags for plain text rendering
22
- const stripHtmlTags = (text) => {
23
- if (!text) {
24
- return text;
25
- }
21
+ // Invisible container whose only job is to make Emotion inject CSS for all Token variants.
22
+ // renderToString produces correct class names but never triggers Emotion's DOM-side injection,
23
+ // so without this the class names exist in the HTML but have no matching CSS rules.
24
+ const HiddenCssPrimer = styled('div')(() => ({
25
+ display: 'none',
26
+ position: 'absolute',
27
+ visibility: 'hidden',
28
+ pointerEvents: 'none',
29
+ }));
30
+
31
+ const normalizeCommonEntities = (text = '') => text.replace(/&nbsp;/gi, '\u00a0');
26
32
 
27
- return text.replace(/<[^>]+>/g, '');
28
- };
33
+ const normalizeSelectableText = (text = '') =>
34
+ normalizeCommonEntities(text)
35
+ .replace(/<\/p>\s*<p[^>]*>/gi, '\n\n')
36
+ .replace(/<br\s*\/?>/gi, '\n')
37
+ .replace(/<\/?(table|tbody|tr|td|p)[^>]*>/gi, '');
29
38
 
30
39
  export class TokenSelect extends React.Component {
31
40
  static propTypes = {
@@ -93,76 +102,139 @@ export class TokenSelect extends React.Component {
93
102
  }
94
103
  };
95
104
 
96
- /** Build a React tree instead of an HTML string so Emotion can inject CSS */
97
- generateTokensNodes = () => {
105
+ /**
106
+ * Build an HTML string so that non-selectable token text (which may contain arbitrary or even
107
+ * *partial* HTML — e.g. just an opening <table><tbody><tr><td> in one token and the matching
108
+ * closing tags in another) is preserved exactly as-is. Selectable Token components are
109
+ * serialised via renderToString; their Emotion class names are stable hashes so they match the
110
+ * CSS that the HiddenCssPrimer forces Emotion to inject into the document.
111
+ */
112
+ generateTokensInHtml = () => {
98
113
  const { tokens, disabled, highlightChoices, animationsDisabled } = this.props;
99
114
  const selectedCount = this.selectedCount();
100
115
 
101
- const isLineBreak = (text) => text === '\n';
102
- const isNewParagraph = (text) => text === '\n\n';
103
-
104
- const paragraphs = [];
105
- let currentChildren = [];
106
-
107
- const flushParagraph = () => {
108
- // Always push a <p>, even if empty, to mirror previous behavior
109
- paragraphs.push(<p key={`p-${paragraphs.length}`}>{currentChildren}</p>);
110
- currentChildren = [];
111
- };
112
-
113
- (tokens || []).forEach((t, index) => {
116
+ const reducer = (accumulator, t, index) => {
114
117
  const selectable = t.selected || (t.selectable && this.canSelectMore(selectedCount));
115
118
  const showCorrectAnswer = t.correct !== undefined && (t.selectable || t.selected);
116
119
 
117
- if (isNewParagraph(t.text)) {
118
- flushParagraph();
119
- return;
120
- }
120
+ if (t.text === '\n\n') return `${accumulator}</p><p>`;
121
121
 
122
- if (isLineBreak(t.text)) {
123
- currentChildren.push(<br key={`br-${index}`} />);
124
- return;
125
- }
122
+ if (t.text === '\n') return `${accumulator}<br>`;
126
123
 
127
124
  if (
128
125
  (selectable && !disabled) ||
129
126
  showCorrectAnswer ||
130
127
  t.selected ||
131
128
  t.isMissing ||
132
- (animationsDisabled && t.predefined) // print mode
129
+ (animationsDisabled && t.predefined)
133
130
  ) {
134
- currentChildren.push(
135
- <Token
136
- key={index}
137
- disabled={disabled}
138
- index={index}
139
- {...t}
140
- text={stripHtmlTags(t.text)}
141
- selectable={selectable}
142
- highlight={highlightChoices}
143
- animationsDisabled={animationsDisabled}
144
- />,
131
+ return (
132
+ accumulator +
133
+ renderToString(
134
+ <Token
135
+ key={index}
136
+ disabled={disabled}
137
+ index={index}
138
+ {...t}
139
+ text={normalizeSelectableText(t.text)}
140
+ selectable={selectable}
141
+ highlight={highlightChoices}
142
+ animationsDisabled={animationsDisabled}
143
+ />,
144
+ )
145
145
  );
146
- } else {
147
- // raw text node – React will escape as needed
148
- currentChildren.push(<React.Fragment key={index}>{stripHtmlTags(t.text)}</React.Fragment>);
149
146
  }
150
- });
151
147
 
152
- // flush last paragraph
153
- flushParagraph();
148
+ // Non-selectable: emit raw HTML unchanged (may contain partial tags, tables, lists, etc.)
149
+ return accumulator + normalizeCommonEntities(t.text);
150
+ };
154
151
 
155
- return paragraphs;
152
+ return (tokens || []).reduce(reducer, '<p>') + '</p>';
156
153
  };
157
154
 
158
155
  render() {
159
156
  const { className: classNameProp } = this.props;
160
- const nodes = this.generateTokensNodes();
157
+ const html = this.generateTokensInHtml();
161
158
 
159
+ // Render one invisible Token per visual variant so Emotion injects all CSS rules into the
160
+ // document before the browser paints the dangerouslySetInnerHTML content.
161
+ const primerText = ' ';
162
162
  return (
163
- <StyledTokenSelect className={classNameProp} onClick={this.toggleToken}>
164
- {nodes}
165
- </StyledTokenSelect>
163
+ <>
164
+ <HiddenCssPrimer aria-hidden="true">
165
+ {/* base / selectable */}
166
+ <Token
167
+ text={primerText}
168
+ index={-1}
169
+ selectable
170
+ disabled={false}
171
+ highlight={false}
172
+ animationsDisabled={false}
173
+ />
174
+ {/* highlight */}
175
+ <Token text={primerText} index={-1} selectable disabled={false} highlight animationsDisabled={false} />
176
+ {/* selected */}
177
+ <Token
178
+ text={primerText}
179
+ index={-1}
180
+ selectable
181
+ selected
182
+ disabled={false}
183
+ highlight={false}
184
+ animationsDisabled={false}
185
+ />
186
+ {/* disabled + selected */}
187
+ <Token
188
+ text={primerText}
189
+ index={-1}
190
+ selectable
191
+ selected
192
+ disabled
193
+ highlight={false}
194
+ animationsDisabled={false}
195
+ />
196
+ {/* print / animationsDisabled */}
197
+ <Token
198
+ text={primerText}
199
+ index={-1}
200
+ selectable
201
+ disabled={false}
202
+ highlight={false}
203
+ animationsDisabled
204
+ predefined
205
+ />
206
+ {/* correct */}
207
+ <Token
208
+ text={primerText}
209
+ index={-1}
210
+ selectable
211
+ selected
212
+ correct
213
+ disabled={false}
214
+ highlight={false}
215
+ animationsDisabled={false}
216
+ />
217
+ {/* incorrect */}
218
+ <Token
219
+ text={primerText}
220
+ index={-1}
221
+ selectable
222
+ selected
223
+ correct={false}
224
+ disabled={false}
225
+ highlight={false}
226
+ animationsDisabled={false}
227
+ />
228
+ {/* missing */}
229
+ <Token text={primerText} index={-1} isMissing disabled={false} highlight={false} animationsDisabled={false} />
230
+ </HiddenCssPrimer>
231
+
232
+ <StyledTokenSelect
233
+ className={classNameProp}
234
+ onClick={this.toggleToken}
235
+ dangerouslySetInnerHTML={{ __html: html }}
236
+ />
237
+ </>
166
238
  );
167
239
  }
168
240
  }