TextSpec.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. import {
  2. assign,
  3. pick
  4. } from 'min-dash';
  5. import {
  6. append as svgAppend,
  7. attr as svgAttr,
  8. create as svgCreate
  9. } from 'tiny-svg';
  10. function toFitBBox(actual, expected) {
  11. var actualBBox = actual.getBBox ? actual.getBBox() : actual;
  12. var pass = actualBBox.width <= expected.width &&
  13. (expected.height ? actualBBox.height <= expected.height : true);
  14. if (actualBBox.x) {
  15. pass = actualBBox.x >= expected.x &&
  16. actualBBox.y >= expected.y &&
  17. actualBBox.x + actualBBox.width <= expected.x + expected.width &&
  18. (expected.height ? actualBBox.y + actualBBox.height <= expected.y + expected.height : true);
  19. }
  20. if (!pass) {
  21. var bbox = pick(actualBBox, ['x', 'y', 'width', 'height']);
  22. var message =
  23. 'Expected Element#' + actual.id + ' with bbox ' +
  24. JSON.stringify(bbox) + ' to fit ' +
  25. JSON.stringify(expected);
  26. console.error(message);
  27. }
  28. return !!pass;
  29. }
  30. import TextUtil from 'lib/util/Text';
  31. import TestContainer from 'mocha-test-container-support';
  32. describe('util - Text', function() {
  33. var container;
  34. var options = {
  35. size: { width: 100 },
  36. padding: 0,
  37. style: {
  38. fontSize: '14px'
  39. }
  40. };
  41. var textUtil = new TextUtil(options);
  42. beforeEach(function() {
  43. var testContainer = TestContainer.get(this);
  44. var parent = document.createElement('div');
  45. parent.style.width = '200px';
  46. parent.style.height = '400px';
  47. parent.style.display = 'inline-block';
  48. testContainer.appendChild(parent);
  49. var svg = svgCreate('svg');
  50. svgAttr(svg, { width: '100%', height: '100%' });
  51. svgAppend(parent, svg);
  52. container = svgCreate('g');
  53. svgAppend(svg, container);
  54. });
  55. function drawRect(bounds, style) {
  56. var x = bounds.x || 0,
  57. y = bounds.y || 0,
  58. width = bounds.width,
  59. height = bounds.height;
  60. var attrs = assign({
  61. fill: 'none',
  62. strokeWidth: 1,
  63. stroke: 'black'
  64. }, style, {
  65. x: x,
  66. y: y,
  67. width: width,
  68. height: height
  69. });
  70. var rect = svgCreate('rect');
  71. svgAttr(rect, attrs);
  72. svgAppend(container, rect);
  73. }
  74. function createText(container, label, options) {
  75. var box = assign({}, { width: 150, height: 50 }, options.box || {});
  76. drawRect(box, { strokeWidth: '3px', stroke: '#CCC' });
  77. var textAndBoundingBox = textUtil.layoutText(label, options);
  78. var element = textAndBoundingBox.element,
  79. dimensions = textAndBoundingBox.dimensions;
  80. drawRect(dimensions, { strokeWidth: '1px', stroke: 'fuchsia' });
  81. svgAppend(container, element);
  82. return element;
  83. }
  84. describe('#createText', function() {
  85. it('should create simple label', function() {
  86. // given
  87. var label = 'I am a label';
  88. // when
  89. var text = createText(container, label, { box: { width: 150, height: 100 } });
  90. expect(text).to.exist;
  91. expect(toFitBBox(text, { x: 33, y: -2, width: 84, height: 30 })).to.be.true;
  92. });
  93. it('should create a label with dimensions equal to 0', function() {
  94. // given
  95. var label = 'I am ww mm WM WMW WMWM';
  96. // when
  97. var text = createText(container, label, { box: { width: 0, height: 0 } });
  98. expect(text).to.exist;
  99. expect(toFitBBox(text, { x: -1, y: -2, width: 40, height: 320 })).to.be.true;
  100. });
  101. it('should create a label with 2 letters', function() {
  102. // given
  103. var label = 'I am ww mm WM WMW WMW';
  104. // when
  105. var text = createText(container, label, { box: { width: 25, height: 25 } });
  106. expect(text).to.exist;
  107. expect(toFitBBox(text, { x: -1, y: -2, width: 40, height: 230 })).to.be.true;
  108. });
  109. it('should create text on hidden element', function() {
  110. // given
  111. var label = 'I am a label';
  112. // render on invisible element
  113. svgAttr(container, 'display', 'none');
  114. // when
  115. var text = createText(container, label, { box: { width: 150, height: 100 } });
  116. // make visible (for bounds check)
  117. svgAttr(container, 'display', '');
  118. expect(text).to.exist;
  119. expect(toFitBBox(text, { x: 33, y: -2, width: 84, height: 30 })).to.be.true;
  120. });
  121. it('should show text ending with a hyphen correctly', function() {
  122. // given
  123. var label = 'VeryVeryVeryVeryVeryVeryVeryVeryVeryLongString-';
  124. // when
  125. var text = createText(container, label, { box: { width: 150, height: 100 } });
  126. // then
  127. expect(text).to.exist;
  128. expect(text.textContent.substr(-1)).equals('-');
  129. });
  130. describe('should line break', function() {
  131. it('at word boundary', function() {
  132. // given
  133. var label = 'I am a long label that should break on spaces';
  134. // when
  135. var text = createText(container, label, { box: { width: 150, height: 100 } });
  136. expect(text).to.exist;
  137. expect(toFitBBox(text, { x: 0, y: -2, width: 160, height: 70 })).to.be.true;
  138. });
  139. it('forced', function() {
  140. // given
  141. var label = 'I_am_a_long_label_that_should_break_forcibly';
  142. // when
  143. var text = createText(container, label, { box: { width: 150, height: 100 } });
  144. expect(text).to.exist;
  145. expect(toFitBBox(text, { x: 0, y: -2, width: 160, height: 70 })).to.be.true;
  146. });
  147. it('forced / break on hyphen', function() {
  148. // given
  149. var label = 'I_am_a_lo-ng_label_that_sho-uld_break_on_hyphens';
  150. // when
  151. var text = createText(container, label, { box: { width: 150, height: 100 } });
  152. expect(text).to.exist;
  153. expect(toFitBBox(text, { x: 0, y: -2, width: 150, height: 100 })).to.be.true;
  154. });
  155. it('preformated / spaced / hyphenated', function() {
  156. // given
  157. var label = 'I am\n\nnatural language\nwith some superdooooper-longwordinthe-middle';
  158. // when
  159. var text = createText(container, label, {
  160. box: { width: 100, height: 100 },
  161. align: 'center-middle',
  162. padding: 5
  163. });
  164. expect(text).to.exist;
  165. expect(toFitBBox(text, { x: 0, y: -40, width: 100, height: 190 })).to.be.true;
  166. });
  167. it('mass hyphenated', function() {
  168. // given
  169. var label = 'Some superdooooper-long-word in the middle';
  170. // when
  171. var text = createText(container, label, {
  172. box: { width: 100, height: 100 },
  173. align: 'center-middle',
  174. padding: 5
  175. });
  176. expect(text).to.exist;
  177. expect(toFitBBox(text, { x: 0, y: -2, width: 100, height: 100 })).to.be.true;
  178. });
  179. it('preformated using line breaks', function() {
  180. // given
  181. var label = 'I am\na long label that\r\nshould break on line breaks';
  182. // when
  183. var text = createText(container, label, { box: { width: 150, height: 100 } });
  184. expect(text).to.exist;
  185. expect(toFitBBox(text, { x: 2, y: -2, width: 150, height: 105 })).to.be.true;
  186. });
  187. it('multiple line breaks', function() {
  188. // given
  189. var label = 'Line breaks line breaks line\n\n\nBreaks';
  190. // when
  191. var text = createText(container, label, { box: { width: 150, height: 100 } });
  192. expect(text.getBBox().height).to.be.above(50);
  193. });
  194. });
  195. describe('should align', function() {
  196. it('center-middle (fixed box)', function() {
  197. // given
  198. var label = 'I am a long label that should break on spaces';
  199. // when
  200. var text = createText(container, label, {
  201. box: { width: 100, height: 100 },
  202. align: 'center-middle',
  203. padding: 5
  204. });
  205. expect(text).to.exist;
  206. expect(toFitBBox(text, { x: 0, y: -2, width: 100, height: 100 })).to.be.true;
  207. });
  208. it('center-middle with padding (fixed box)', function() {
  209. // given
  210. var label = 'I am a long label that should break on spaces';
  211. // when
  212. var text = createText(container, label, {
  213. box: { width: 100, height: 100 },
  214. align: 'center-middle',
  215. padding: 15
  216. });
  217. expect(text).to.exist;
  218. expect(toFitBBox(text, { x: 15, y: -11, width: 73, height: 127 })).to.be.true;
  219. });
  220. it('center-middle / preformated using line breaks (fixed box)', function() {
  221. // given
  222. var label = 'I am\na long label that\r\nshould break on line breaks';
  223. // when
  224. var text = createText(container, label, {
  225. box: { width: 100, height: 100 },
  226. align: 'center-middle',
  227. padding: 5
  228. });
  229. expect(text).to.exist;
  230. expect(toFitBBox(text, { x: 0, y: -10, width: 100, height: 130 })).to.be.true;
  231. });
  232. it('center-middle / vertical float out (fixed box)', function() {
  233. // given
  234. var label = 'I am a long label that should break on spaces and float out of the container';
  235. // when
  236. var text = createText(container, label, {
  237. box: { width: 100, height: 100 },
  238. align: 'center-middle',
  239. padding: 5
  240. });
  241. expect(text).to.exist;
  242. expect(toFitBBox(text, { x: 0, y: -30, width: 100, height: 180 })).to.be.true;
  243. });
  244. it('left-middle (fixed box)', function() {
  245. // given
  246. var label = 'I am a long label that should break on spaces and float out of the container';
  247. // when
  248. var text = createText(container, label, {
  249. box: { width: 100, height: 100 },
  250. align: 'left-middle',
  251. padding: 5
  252. });
  253. expect(text).to.exist;
  254. expect(toFitBBox(text, { x: 0, y: -30, width: 100, height: 180 })).to.be.true;
  255. });
  256. it('center-middle (fit box)', function() {
  257. // given
  258. var label = 'I am tiny';
  259. // when
  260. var text = createText(container, label, {
  261. box: { width: 100, height: 100 },
  262. fitBox: true,
  263. align: 'center-middle',
  264. padding: 5
  265. });
  266. expect(text).to.exist;
  267. expect(toFitBBox(text, { x: 0, y: -2, width: 150, height: 100 })).to.be.true;
  268. });
  269. it('center-middle / vertical float out (fit box)', function() {
  270. // given
  271. var label = 'I am a long label that should break on spaces and float out of the container';
  272. // when
  273. var text = createText(container, label, {
  274. box: { width: 100, height: 100 },
  275. fitBox: true,
  276. align: 'center-middle',
  277. padding: 5
  278. });
  279. expect(text).to.exist;
  280. expect(toFitBBox(text, { x: 0, y: -25, width: 100, height: 150 })).to.be.true;
  281. });
  282. it('left-middle (fit box)', function() {
  283. // given
  284. var label = 'I am a long label that should break on spaces and float out of the container';
  285. // when
  286. var text = createText(container, label, {
  287. box: { width: 100, height: 100 },
  288. align: 'left-middle',
  289. padding: 5
  290. });
  291. expect(text).to.exist;
  292. expect(toFitBBox(text, { x: 0, y: -25, width: 100, height: 150 })).to.be.true;
  293. });
  294. });
  295. describe('should style', function() {
  296. it('with custom size / weight / color / center-middle', function() {
  297. // given
  298. var label = 'I am a\nstyled\nlabel that will float';
  299. var style = {
  300. fill: 'fuchsia',
  301. fontWeight: 'bold',
  302. fontFamily: 'Arial',
  303. fontSize: '13pt'
  304. };
  305. // when
  306. var text = createText(container, label, {
  307. box: { width: 100, height: 100 },
  308. style: style,
  309. padding: 5,
  310. align: 'center-middle'
  311. });
  312. expect(text).to.exist;
  313. expect(toFitBBox(text, { x: 5, y: -5, width: 96, height: 110 })).to.be.true;
  314. });
  315. it('with custom size / weight / color / center-top', function() {
  316. // given
  317. var label = 'I am a\nstyled\nlabel that will float';
  318. var style = {
  319. fill: 'fuchsia',
  320. fontWeight: 'bold',
  321. fontFamily: 'Arial',
  322. fontSize: '13pt'
  323. };
  324. // when
  325. var text = createText(container, label, {
  326. box: { width: 100, height: 100 },
  327. style: style,
  328. align: 'center-top'
  329. });
  330. expect(text).to.exist;
  331. expect(toFitBBox(text, { x: 0, y: -2, width: 102, height: 100 })).to.be.true;
  332. });
  333. it('big / center-top', function() {
  334. // given
  335. var label = 'I am a style';
  336. var style = {
  337. fill: 'fuchsia',
  338. fontWeight: 'bold',
  339. fontFamily: 'Arial',
  340. fontSize: '28px'
  341. };
  342. // when
  343. var text = createText(container, label, {
  344. box: { width: 100, height: 100 },
  345. style: style,
  346. align: 'center-top'
  347. });
  348. expect(text).to.exist;
  349. expect(toFitBBox(text, { x: 0, y: -2, width: 100, height: 100 })).to.be.true;
  350. });
  351. it('custom line-height / center-top', function() {
  352. // given
  353. var label = 'I am a style';
  354. var style = {
  355. fill: 'fuchsia',
  356. fontWeight: 'bold',
  357. fontFamily: 'Arial',
  358. fontSize: '20px',
  359. lineHeight: 3
  360. };
  361. // when
  362. var text = createText(container, label, {
  363. box: { width: 100, height: 100 },
  364. style: style,
  365. align: 'center-top'
  366. });
  367. expect(text).to.exist;
  368. expect(toFitBBox(text, { x: 12, y: 20, width: 85, height: 95 })).to.be.true;
  369. });
  370. it('custom line-height / center-middle', function() {
  371. // given
  372. var label = 'I am a style';
  373. var style = {
  374. fill: 'fuchsia',
  375. fontWeight: 'bold',
  376. fontFamily: 'Arial',
  377. fontSize: '20px',
  378. lineHeight: 3
  379. };
  380. // when
  381. var text = createText(container, label, {
  382. box: { width: 100, height: 100 },
  383. style: style,
  384. align: 'center-middle'
  385. });
  386. expect(text).to.exist;
  387. expect(toFitBBox(text, { x: 13, y: 10, width: 80, height: 90 })).to.be.true;
  388. });
  389. });
  390. });
  391. describe('#getDimensions', function() {
  392. it('should get bounding box of simple label', function() {
  393. // given
  394. var label = 'I am a label';
  395. var box = {
  396. width: 100,
  397. height: 100
  398. };
  399. // when
  400. var dimensions = textUtil.getDimensions(label, { box: box });
  401. // then
  402. expect(dimensions).to.exist;
  403. expect(toFitBBox(dimensions, { width: 100, height: 20 })).to.be.true;
  404. });
  405. it('should get bounding box of multi line label', function() {
  406. // given
  407. var label = 'I am a\nlabel';
  408. var box = {
  409. width: 100,
  410. height: 100
  411. };
  412. // when
  413. var dimensions = textUtil.getDimensions(label, { box: box });
  414. // then
  415. expect(dimensions).to.exist;
  416. expect(toFitBBox(dimensions, { width: 100, height: 40 })).to.be.true;
  417. });
  418. });
  419. });