@icij/murmur-next 4.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.
Files changed (296) hide show
  1. package/.github/workflows/deploy-github-pages.yaml +50 -0
  2. package/.storybook/app.scss +14 -0
  3. package/.storybook/doc_variables.scss +20 -0
  4. package/.storybook/main.ts +35 -0
  5. package/.storybook/preview-head.html +2 -0
  6. package/.storybook/preview.ts +32 -0
  7. package/README.md +71 -0
  8. package/deploy.js +15 -0
  9. package/docs/components/ApiTable.vue +171 -0
  10. package/docs/components/App.vue +146 -0
  11. package/docs/components/CollapsibleBlock.vue +122 -0
  12. package/docs/components/DocsHeader.vue +68 -0
  13. package/docs/components/DocsMenu.vue +201 -0
  14. package/docs/components/DocsMenuSection.vue +109 -0
  15. package/docs/components/EditLink.vue +49 -0
  16. package/docs/components/OutboundLink.vue +13 -0
  17. package/docs/components/PalettePresenter.vue +96 -0
  18. package/docs/components/RepositoryLink.vue +28 -0
  19. package/docs/components/SampleCard.vue +119 -0
  20. package/docs/main.js +42 -0
  21. package/docs/pages/components/accordion/doc.md +96 -0
  22. package/docs/pages/components/active-text-truncate/doc.md +44 -0
  23. package/docs/pages/components/advanced-link-form/doc.md +105 -0
  24. package/docs/pages/components/brand/doc.md +30 -0
  25. package/docs/pages/components/brand-expansion/doc.md +70 -0
  26. package/docs/pages/components/confirm-button/doc.md +91 -0
  27. package/docs/pages/components/content-placeholder/doc.md +16 -0
  28. package/docs/pages/components/custom-pagination/doc.md +61 -0
  29. package/docs/pages/components/digits-input/doc.md +28 -0
  30. package/docs/pages/components/donate-form/doc.md +20 -0
  31. package/docs/pages/components/embed-form/doc.md +22 -0
  32. package/docs/pages/components/embeddable-footer/doc.md +60 -0
  33. package/docs/pages/components/follow-us-popover/doc.md +5 -0
  34. package/docs/pages/components/generic-footer/doc.md +21 -0
  35. package/docs/pages/components/generic-header/doc.md +24 -0
  36. package/docs/pages/components/haptic-copy/doc.md +27 -0
  37. package/docs/pages/components/imddb-header/doc.md +23 -0
  38. package/docs/pages/components/ordinal-legend/doc.md +44 -0
  39. package/docs/pages/components/range-picker/doc.md +86 -0
  40. package/docs/pages/components/responsive-iframe/doc.md +13 -0
  41. package/docs/pages/components/scale-legend/doc.md +65 -0
  42. package/docs/pages/components/secret-input/doc.md +12 -0
  43. package/docs/pages/components/selectable-dropdown/doc.md +156 -0
  44. package/docs/pages/components/sharing-options/doc.md +13 -0
  45. package/docs/pages/components/sharing-options-link/doc.md +36 -0
  46. package/docs/pages/components/sign-up-form/doc.md +13 -0
  47. package/docs/pages/components/slide-up-down/doc.md +28 -0
  48. package/docs/pages/components/textured-deck/doc.md +78 -0
  49. package/docs/pages/components/tiny-pagination/doc.md +92 -0
  50. package/docs/pages/datavisualisation/bars/doc.md +110 -0
  51. package/docs/pages/datavisualisation/columns/doc.md +165 -0
  52. package/docs/pages/datavisualisation/lines/doc.md +139 -0
  53. package/docs/pages/datavisualisation/stacked-bar/doc.md +160 -0
  54. package/docs/pages/datavisualisation/stacked-column/doc.md +191 -0
  55. package/docs/pages/getting-started/about-icij/doc.md +13 -0
  56. package/docs/pages/getting-started/custom-bootstrap/doc.md +36 -0
  57. package/docs/pages/getting-started/installation-guide/doc.md +59 -0
  58. package/docs/pages/getting-started/internationalization/doc.md +74 -0
  59. package/docs/pages/maps/choropleth-map/doc.md +420 -0
  60. package/docs/pages/maps/choropleth-map-annotation/doc.md +373 -0
  61. package/docs/pages/maps/symbol-map/doc.md +203 -0
  62. package/docs/pages/structure/breakpoints/doc.md +3 -0
  63. package/docs/pages/structure/grid/doc.md +3 -0
  64. package/docs/pages/utilities/assets/doc.md +138 -0
  65. package/docs/pages/utilities/config/doc.md +52 -0
  66. package/docs/pages/utilities/iframes/doc.md +3 -0
  67. package/docs/pages/visual/colors/doc.md +31 -0
  68. package/docs/pages/visual/iconography/doc.md +56 -0
  69. package/docs/pages/visual/states/doc.md +77 -0
  70. package/docs/pages/visual/themes/doc.md +3 -0
  71. package/docs/pages/visual/typography/doc.md +71 -0
  72. package/docs/routes.js +25 -0
  73. package/docs/store/index.js +21 -0
  74. package/docs/styles/app.scss +36 -0
  75. package/docs/styles/variables.scss +20 -0
  76. package/lib/assets/images/icij-full-white.svg +6 -0
  77. package/lib/assets/images/icij-full.svg +6 -0
  78. package/lib/assets/images/icij.png +0 -0
  79. package/lib/assets/images/icij.svg +46 -0
  80. package/lib/assets/images/icij@2x.png +0 -0
  81. package/lib/assets/images/murmur-dark.png +0 -0
  82. package/lib/assets/images/murmur-dark.svg +79 -0
  83. package/lib/assets/images/murmur-white.png +0 -0
  84. package/lib/assets/images/murmur-white.svg +68 -0
  85. package/lib/components/AccordionStep.vue +128 -0
  86. package/lib/components/AccordionWrapper.vue +138 -0
  87. package/lib/components/ActiveTextTruncate.vue +258 -0
  88. package/lib/components/AdvancedLinkForm.vue +273 -0
  89. package/lib/components/Brand.vue +150 -0
  90. package/lib/components/BrandExpansion.vue +237 -0
  91. package/lib/components/ConfirmButton.vue +204 -0
  92. package/lib/components/ContentPlaceholder.vue +100 -0
  93. package/lib/components/CustomPagination.vue +225 -0
  94. package/lib/components/DigitsInput.vue +180 -0
  95. package/lib/components/DonateForm.vue +367 -0
  96. package/lib/components/EmbedForm.vue +173 -0
  97. package/lib/components/EmbeddableFooter.vue +201 -0
  98. package/lib/components/Fa.js +3 -0
  99. package/lib/components/FollowUsPopover.vue +117 -0
  100. package/lib/components/GenericFooter.vue +218 -0
  101. package/lib/components/GenericHeader.vue +259 -0
  102. package/lib/components/HapticCopy.vue +256 -0
  103. package/lib/components/ImddbHeader.vue +336 -0
  104. package/lib/components/OrdinalLegend.vue +164 -0
  105. package/lib/components/RangePicker.vue +430 -0
  106. package/lib/components/ResponsiveIframe.vue +48 -0
  107. package/lib/components/ScaleLegend.vue +230 -0
  108. package/lib/components/SecretInput.vue +132 -0
  109. package/lib/components/SelectableDropdown.vue +368 -0
  110. package/lib/components/SharingOptions.vue +230 -0
  111. package/lib/components/SharingOptionsLink.vue +259 -0
  112. package/lib/components/SignUpForm.vue +181 -0
  113. package/lib/components/SlideUpDown.vue +131 -0
  114. package/lib/components/TexturedDeck.vue +101 -0
  115. package/lib/components/TinyPagination.vue +268 -0
  116. package/lib/components/index.js +31 -0
  117. package/lib/composables/chart.ts +182 -0
  118. package/lib/composables/resizeObserver.ts +37 -0
  119. package/lib/composables/sendEmail.ts +50 -0
  120. package/lib/config.default.ts +33 -0
  121. package/lib/config.ts +70 -0
  122. package/lib/d3-geo-projection.d.ts +1 -0
  123. package/lib/datavisualisations/BarChart.vue +275 -0
  124. package/lib/datavisualisations/ColumnChart.vue +527 -0
  125. package/lib/datavisualisations/LineChart.vue +274 -0
  126. package/lib/datavisualisations/StackedBarChart.vue +614 -0
  127. package/lib/datavisualisations/StackedColumnChart.vue +640 -0
  128. package/lib/datavisualisations/index.js +5 -0
  129. package/lib/enums.ts +25 -0
  130. package/lib/i18n.ts +16 -0
  131. package/lib/keys.ts +2 -0
  132. package/lib/locales/en.json +140 -0
  133. package/lib/locales/fr.json +117 -0
  134. package/lib/locales/locales/en.json +140 -0
  135. package/lib/locales/locales/fr.json +117 -0
  136. package/lib/main.ts +87 -0
  137. package/lib/maps/ChoroplethMap.vue +825 -0
  138. package/lib/maps/ChoroplethMapAnnotation.vue +336 -0
  139. package/lib/maps/SymbolMap.vue +628 -0
  140. package/lib/maps/index.js +3 -0
  141. package/lib/querystring-es3.d.ts +1 -0
  142. package/lib/shims-bootstrap-vue.d.ts +5 -0
  143. package/lib/shims-tsx.d.ts +11 -0
  144. package/lib/shims-vue.d.ts +14 -0
  145. package/lib/styles/functions.scss +20 -0
  146. package/lib/styles/lib.scss +19 -0
  147. package/lib/styles/mixins.scss +37 -0
  148. package/lib/styles/utilities.scss +18 -0
  149. package/lib/styles/variables.scss +94 -0
  150. package/lib/styles/variables_dark.scss +1 -0
  151. package/lib/types.ts +46 -0
  152. package/lib/utils/animation.ts +24 -0
  153. package/lib/utils/assets.ts +46 -0
  154. package/lib/utils/clipboard.ts +41 -0
  155. package/lib/utils/iframe-resizer.ts +49 -0
  156. package/lib/utils/placeholder.ts +66 -0
  157. package/lib/utils/placeholderTypes.ts +21 -0
  158. package/lib/utils/strings.ts +8 -0
  159. package/loaders/highlight-loader.js +13 -0
  160. package/loaders/markdown-loader.js +91 -0
  161. package/loaders/metadata-loader.js +18 -0
  162. package/loaders/sass-extract-loader.js +14 -0
  163. package/loaders/vue-docgen-loader.js +14 -0
  164. package/package.json +96 -0
  165. package/plugins/MdPluginTypes.ts +10 -0
  166. package/plugins/docs.ts +50 -0
  167. package/plugins/front-matter.ts +36 -0
  168. package/plugins/highlight.ts +27 -0
  169. package/plugins/markdown-it/api-table.ts +25 -0
  170. package/plugins/markdown-it/sample-card.ts +31 -0
  171. package/plugins/plugin-delete.ts +47 -0
  172. package/plugins/plugin-docgen.ts +23 -0
  173. package/plugins/sass-vars.ts +25 -0
  174. package/plugins/vue-docgen.ts +29 -0
  175. package/public/android-chrome-192x192.png +0 -0
  176. package/public/android-chrome-512x512.png +0 -0
  177. package/public/apple-touch-icon.png +0 -0
  178. package/public/assets/img/arrow-bottom.svg +3 -0
  179. package/public/assets/img/texture-brick-black.jpg +0 -0
  180. package/public/assets/img/texture-brick.jpg +0 -0
  181. package/public/assets/img/texture-carbon-black.jpg +0 -0
  182. package/public/assets/img/texture-carbon.jpg +0 -0
  183. package/public/assets/img/texture-crack-black.jpg +0 -0
  184. package/public/assets/img/texture-crack.jpg +0 -0
  185. package/public/assets/img/texture-rock-black.jpg +0 -0
  186. package/public/assets/img/texture-rock.jpg +0 -0
  187. package/public/assets/img/texture-sand-black.jpg +0 -0
  188. package/public/assets/img/texture-sand.jpg +0 -0
  189. package/public/assets/img/texture-silk-black.jpg +0 -0
  190. package/public/assets/img/texture-silk.jpg +0 -0
  191. package/public/assets/topojson/france-departments.json +1 -0
  192. package/public/assets/topojson/paris-arrondissements.json +1 -0
  193. package/public/assets/topojson/world-countries-sans-antarctica.json +1 -0
  194. package/public/favicon-16x16.png +0 -0
  195. package/public/favicon-32x32.png +0 -0
  196. package/public/favicon.ico +0 -0
  197. package/public/site.webmanifest +1 -0
  198. package/stories/assets/code-brackets.svg +1 -0
  199. package/stories/assets/colors.svg +1 -0
  200. package/stories/assets/comments.svg +1 -0
  201. package/stories/assets/direction.svg +1 -0
  202. package/stories/assets/flow.svg +1 -0
  203. package/stories/assets/plugin.svg +1 -0
  204. package/stories/assets/repo.svg +1 -0
  205. package/stories/assets/stackalt.svg +1 -0
  206. package/stories/getting-started/about-icij.mdx +14 -0
  207. package/stories/getting-started/custom-bootstrap.mdx +23 -0
  208. package/stories/getting-started/installation-guide.mdx +62 -0
  209. package/stories/getting-started/internationalization.mdx +63 -0
  210. package/stories/murmur/components/AccordionStep.stories.ts +33 -0
  211. package/stories/murmur/components/AccordionWrapper.stories.ts +69 -0
  212. package/stories/murmur/components/ActiveTextTruncate.stories.ts +32 -0
  213. package/stories/murmur/components/AdvancedLinkForm.stories.ts +77 -0
  214. package/stories/murmur/components/Brand.stories.ts +30 -0
  215. package/stories/murmur/components/BrandExpansion.stories.ts +41 -0
  216. package/stories/murmur/components/ConfirmButton.stories.ts +40 -0
  217. package/stories/murmur/components/ContentPlaceholder.stories.ts +41 -0
  218. package/stories/murmur/components/CustomPagination.stories.ts +42 -0
  219. package/stories/murmur/components/DigitsInput.stories.ts +29 -0
  220. package/stories/murmur/components/DonateForm.stories.ts +29 -0
  221. package/stories/murmur/components/EmbedForm.stories.ts +35 -0
  222. package/stories/murmur/components/EmbeddableFooter.stories.ts +59 -0
  223. package/stories/murmur/components/FollowUsPopover.stories.ts +24 -0
  224. package/stories/murmur/components/GenericFooter.stories.ts +27 -0
  225. package/stories/murmur/components/GenericHeader.stories.ts +27 -0
  226. package/stories/murmur/components/HapticCopy.stories.ts +40 -0
  227. package/stories/murmur/components/ImddbHeader.stories.ts +27 -0
  228. package/stories/murmur/components/OrdinalLegend.stories.ts +49 -0
  229. package/stories/murmur/components/RangePicker.stories.ts +98 -0
  230. package/stories/murmur/components/ResponsiveIframe.stories.ts +24 -0
  231. package/stories/murmur/components/ScaleLegend.stories.ts +65 -0
  232. package/stories/murmur/components/SecretInput.stories.ts +60 -0
  233. package/stories/murmur/components/SelectableDropdown.stories.ts +143 -0
  234. package/stories/murmur/components/SharingOptions.stories.ts +32 -0
  235. package/stories/murmur/components/SharingOptionsLink.stories.ts +53 -0
  236. package/stories/murmur/components/SignUpForm.stories.ts +51 -0
  237. package/stories/murmur/components/SlideUpDown.stories.ts +32 -0
  238. package/stories/murmur/components/TexturedDeck.stories.ts +83 -0
  239. package/stories/murmur/components/TinyPagination.stories.ts +65 -0
  240. package/stories/murmur/datavisualisations/BarChart.stories.ts +54 -0
  241. package/stories/murmur/datavisualisations/ColumnChart.stories.ts +88 -0
  242. package/stories/murmur/datavisualisations/LineChart.stories.ts +139 -0
  243. package/stories/murmur/datavisualisations/StackedBarChart.stories.ts +199 -0
  244. package/stories/murmur/datavisualisations/StackedColumnChart.stories.ts +136 -0
  245. package/stories/murmur/decorators.ts +108 -0
  246. package/stories/murmur/maps/ChoroplethMap.stories.ts +440 -0
  247. package/stories/murmur/maps/ChoroplethMapAnnotation.stories.ts +26 -0
  248. package/stories/murmur/maps/SymbolMap.stories.ts +24 -0
  249. package/stories/murmur/utils.ts +7 -0
  250. package/tests/unit/components/AccordionStep.spec.ts +157 -0
  251. package/tests/unit/components/AccordionWrapper.spec.ts +57 -0
  252. package/tests/unit/components/ActiveTextTruncate.spec.js +30 -0
  253. package/tests/unit/components/AdvancedLinkForm.spec.js +124 -0
  254. package/tests/unit/components/Brand.spec.js +50 -0
  255. package/tests/unit/components/ContentPlaceholder.spec.js +29 -0
  256. package/tests/unit/components/CustomPagination.spec.js +72 -0
  257. package/tests/unit/components/DigitsInput.spec.ts +157 -0
  258. package/tests/unit/components/DonateForm.spec.js +149 -0
  259. package/tests/unit/components/EmbedForm.spec.js +108 -0
  260. package/tests/unit/components/EmbeddableFooter.spec.js +11 -0
  261. package/tests/unit/components/Fa.spec.js +18 -0
  262. package/tests/unit/components/FollowUsPopover.spec.js +29 -0
  263. package/tests/unit/components/GenericFooter.spec.js +29 -0
  264. package/tests/unit/components/GenericHeader.spec.js +104 -0
  265. package/tests/unit/components/HapticCopy.spec.js +123 -0
  266. package/tests/unit/components/ImddbHeader.spec.js +96 -0
  267. package/tests/unit/components/OrdinalLegend.spec.js +120 -0
  268. package/tests/unit/components/RangePicker.spec.ts +87 -0
  269. package/tests/unit/components/ResponsiveIframe.spec.js +20 -0
  270. package/tests/unit/components/ScaleLegend.spec.js +139 -0
  271. package/tests/unit/components/SecretInput.spec.js +81 -0
  272. package/tests/unit/components/SelectableDropdown.spec.js +160 -0
  273. package/tests/unit/components/SharingOptions.spec.js +125 -0
  274. package/tests/unit/components/SharingOptionsLink.spec.js +184 -0
  275. package/tests/unit/components/SignUpForm.spec.js +145 -0
  276. package/tests/unit/components/SlideUpDown.spec.js +59 -0
  277. package/tests/unit/components/TinyPagination.spec.js +46 -0
  278. package/tests/unit/config.spec.js +136 -0
  279. package/tests/unit/datavisualisations/BarChart.spec.js +63 -0
  280. package/tests/unit/datavisualisations/ColumnChart.spec.js +344 -0
  281. package/tests/unit/datavisualisations/LineChart.spec.js +155 -0
  282. package/tests/unit/datavisualisations/StackedBarChart.spec.js +294 -0
  283. package/tests/unit/datavisualisations/StackedColumnChart.spec.js +443 -0
  284. package/tests/unit/i18n.spec.ts +19 -0
  285. package/tests/unit/main.spec.js +82 -0
  286. package/tests/unit/maps/ChoroplethMap.spec.js +214 -0
  287. package/tests/unit/maps/ChoroplethMapAnnotation.spec.ts +186 -0
  288. package/tests/unit/maps/SymbolMap.spec.js +92 -0
  289. package/tests/unit/require.spec.js +22 -0
  290. package/tests/unit/setup.js +13 -0
  291. package/tests/unit/utils/assets.spec.js +61 -0
  292. package/tests/unit/utils/clipboard.spec.js +18 -0
  293. package/tests/unit/utils/iframe-resizer.spec.js +71 -0
  294. package/tsconfig.json +35 -0
  295. package/vite.config.ts +79 -0
  296. package/vitest.config.ts +19 -0
@@ -0,0 +1,155 @@
1
+ import * as d3 from 'd3'
2
+ import { mount } from '@vue/test-utils'
3
+ import LineChart from '@/datavisualisations/LineChart.vue'
4
+
5
+ vi.mock('d3', async () => {
6
+ return {
7
+ ...await vi.importActual('d3'),
8
+ }
9
+ })
10
+
11
+ // Mock HTML element offset so the size of the chart can be calculated
12
+ // dynamically using JSDOM and tests
13
+ Object.defineProperties(window.HTMLElement.prototype, {
14
+ offsetWidth: {
15
+ get () { return parseFloat(this.style.width) || 0 }
16
+ },
17
+ offsetHeight: {
18
+ get () { return parseFloat(this.style.height) || 0 }
19
+ }
20
+ })
21
+
22
+ describe('LineChart.vue', () => {
23
+
24
+ describe('a single chart', () => {
25
+
26
+ let wrapper
27
+
28
+ beforeEach(async () => {
29
+
30
+ const propsData = {
31
+ fixedHeight: 300,
32
+ xAxisTicks: d3.timeYear.every(1),
33
+ data: [
34
+ { date: 2000, value: 0 },
35
+ { date: 2001, value: 1 },
36
+ { date: 2002, value: 2 },
37
+ { date: 2003, value: 3 },
38
+ { date: 2004, value: 4 }
39
+ ]
40
+ }
41
+
42
+ wrapper = mount(LineChart, { propsData })
43
+ })
44
+
45
+ it('is a Vue instance', () => {
46
+ expect(wrapper.vm).toBeTruthy()
47
+ })
48
+
49
+ it('is a fixed height chart, regardeless of the mode', async () => {
50
+ expect(wrapper.vm.height).toBe(300)
51
+ await wrapper.setProps({ socialMode: true })
52
+ expect(wrapper.vm.height).toBe(300)
53
+ })
54
+
55
+ it('creates five x-axis ticks', async () => {
56
+ expect(wrapper.findAll('.line-chart__axis--x .tick')).toHaveLength(5)
57
+ })
58
+
59
+ it('creates x-axis ticks with the right years ', async () => {
60
+ const ticks = wrapper.findAll('.line-chart__axis--x .tick')
61
+ expect(ticks.at(0).text()).toBe('2000')
62
+ expect(ticks.at(1).text()).toBe('2001')
63
+ expect(ticks.at(2).text()).toBe('2002')
64
+ expect(ticks.at(3).text()).toBe('2003')
65
+ expect(ticks.at(4).text()).toBe('2004')
66
+ })
67
+ })
68
+
69
+ describe('a two steps line chart using remote JSON', () => {
70
+
71
+ let wrapper
72
+
73
+ beforeEach(async () => {
74
+ d3.json = vi.fn().mockReturnValue([
75
+ { date: 2019, value: 50 },
76
+ { date: 2020, value: 100 }
77
+ ])
78
+
79
+ const propsData = {
80
+ data: 'http://localhost/data.json',
81
+ xAxisTicks: d3.timeYear.every(1)
82
+ }
83
+ wrapper = mount(LineChart, { propsData })
84
+ wrapper.vm.$el.style.width = '500px'
85
+ })
86
+
87
+ it('is a Vue instance', () => {
88
+ expect(wrapper.vm).toBeTruthy()
89
+ })
90
+
91
+ it('creates two x-axis ticks', async () => {
92
+ expect(wrapper.findAll('.line-chart__axis--x .tick')).toHaveLength(2)
93
+ })
94
+
95
+ it('creates x-axis ticks with the right years ', async () => {
96
+ const ticks = wrapper.findAll('.line-chart__axis--x .tick')
97
+ expect(ticks.at(0).text()).toBe('2019')
98
+ expect(ticks.at(1).text()).toBe('2020')
99
+ })
100
+ })
101
+
102
+ describe('a 10 columns chart with two highlights using remote CSV', () => {
103
+
104
+ let wrapper
105
+
106
+ beforeEach(async () => {
107
+ d3.csv = vi.fn().mockReturnValue([
108
+ { date: 2000, indicator: 0, highlight: false },
109
+ { date: 2001, indicator: 10, highlight: false },
110
+ { date: 2002, indicator: 20, highlight: false },
111
+ { date: 2003, indicator: 30, highlight: false },
112
+ { date: 2004, indicator: 40, highlight: false },
113
+ { date: 2005, indicator: 50, highlight: true },
114
+ { date: 2006, indicator: 60, highlight: false },
115
+ { date: 2007, indicator: 70, highlight: true },
116
+ { date: 2008, indicator: 80, highlight: false },
117
+ { date: 2009, indicator: 90, highlight: false }
118
+ ])
119
+
120
+ const propsData = {
121
+ data: 'http://localhost/data.csv',
122
+ dataUrlType: 'csv',
123
+ xAxisTicks: d3.timeYear.every(1),
124
+ seriesName: 'indicator'
125
+ }
126
+ wrapper = mount(LineChart, { propsData })
127
+ })
128
+
129
+ afterEach(async () => {
130
+ wrapper.unmount()
131
+ })
132
+
133
+ it('is a Vue instance', () => {
134
+ expect(wrapper.vm).toBeTruthy()
135
+ })
136
+
137
+ it('creates ten x-axis ticks', () => {
138
+ expect(wrapper.findAll('.line-chart__axis--x .tick')).toHaveLength(10)
139
+ })
140
+
141
+ it('creates x-axis ticks with the right years', () => {
142
+ const ticks = wrapper.findAll('.line-chart__axis--x .tick')
143
+ expect(ticks.at(0).text()).toBe('2000')
144
+ expect(ticks.at(1).text()).toBe('2001')
145
+ expect(ticks.at(2).text()).toBe('2002')
146
+ expect(ticks.at(3).text()).toBe('2003')
147
+ expect(ticks.at(4).text()).toBe('2004')
148
+ expect(ticks.at(5).text()).toBe('2005')
149
+ expect(ticks.at(6).text()).toBe('2006')
150
+ expect(ticks.at(7).text()).toBe('2007')
151
+ expect(ticks.at(8).text()).toBe('2008')
152
+ expect(ticks.at(9).text()).toBe('2009')
153
+ })
154
+ })
155
+ })
@@ -0,0 +1,294 @@
1
+ import { mount, flushPromises } from '@vue/test-utils'
2
+ import StackedBarChart from '@/datavisualisations/StackedBarChart.vue'
3
+
4
+ // Mock HTML element offset so the size of the chart can be calculated
5
+ // dynamicly using JSDOM and tests
6
+ Object.defineProperties(window.HTMLElement.prototype, {
7
+ offsetWidth: {
8
+ get () { return parseFloat(this.style.width) || 0 }
9
+ },
10
+ offsetHeight: {
11
+ get () { return parseFloat(this.style.height) || 0 }
12
+ }
13
+ })
14
+
15
+ describe('StackedBarChart.vue', () => {
16
+
17
+ describe('a stacked-bars chart with two bars in four groups', () => {
18
+
19
+ let wrapper
20
+
21
+ beforeEach(async () => {
22
+ vi.useFakeTimers()
23
+
24
+ const propsData = {
25
+ data: [
26
+ { label: "Avatar", budget: 237, box_office: 2784 },
27
+ { label: "ET: The Extra-Terrestrial", budget: 11, box_office: 793 },
28
+ { label: "Finding Nemo", budget: 94, box_office: 940 },
29
+ { label: "Ghostbusters", budget: 14, box_office: 229 }
30
+ ]
31
+ }
32
+
33
+ wrapper = mount(StackedBarChart, { propsData })
34
+ })
35
+
36
+ afterEach(async () => {
37
+ await vi.runAllTimersAsync()
38
+ vi.useRealTimers()
39
+ })
40
+
41
+ it('is a Vue instance', () => {
42
+ expect(wrapper.vm).toBeTruthy()
43
+ })
44
+
45
+ it('creates four bars', async () => {
46
+ expect(wrapper.findAll('.stacked-bar-chart__groups__item')).toHaveLength(4)
47
+ })
48
+
49
+ it('creates the first group of bars with maximum width', () => {
50
+ const firstGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
51
+ const budgetBar = firstGroup.find('.stacked-bar-chart__groups__item__bars__item--budget')
52
+ const boxOfficeBar = firstGroup.find('.stacked-bar-chart__groups__item__bars__item--box-office')
53
+ const totalWidth = budgetBar.element.offsetWidth + boxOfficeBar.element.offsetWidth
54
+ expect(totalWidth).toBe(100)
55
+ })
56
+
57
+ it('creates the second group of bars with ~27% width', () => {
58
+ const secondGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(1)
59
+ const budgetBar = secondGroup.find('.stacked-bar-chart__groups__item__bars__item--budget')
60
+ const boxOfficeBar = secondGroup.find('.stacked-bar-chart__groups__item__bars__item--box-office')
61
+ const totalWidth = Math.round(budgetBar.element.offsetWidth + boxOfficeBar.element.offsetWidth)
62
+ expect(totalWidth).toBe(27)
63
+ })
64
+
65
+ it('creates the third group of bars with ~34% width', () => {
66
+ const secondGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(2)
67
+ const budgetBar = secondGroup.find('.stacked-bar-chart__groups__item__bars__item--budget')
68
+ const boxOfficeBar = secondGroup.find('.stacked-bar-chart__groups__item__bars__item--box-office')
69
+ const totalWidth = Math.round(budgetBar.element.offsetWidth + boxOfficeBar.element.offsetWidth)
70
+ expect(totalWidth).toBe(34)
71
+ })
72
+
73
+ it('creates the first group of bars with budget taking ~8% width', () => {
74
+ const firstGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
75
+ const budgetBar = firstGroup.find('.stacked-bar-chart__groups__item__bars__item--budget')
76
+ const width = Math.round(budgetBar.element.offsetWidth)
77
+ expect(width).toBe(8)
78
+ })
79
+
80
+ it('creates the first group of bars with box_office taking ~92% width', () => {
81
+ const firstGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
82
+ const boxOfficeBar = firstGroup.find('.stacked-bar-chart__groups__item__bars__item--box-office')
83
+ const width = Math.round(boxOfficeBar.element.offsetWidth)
84
+ expect(width).toBe(92)
85
+ })
86
+
87
+ it('creates the first group with "Avatar" as label', () => {
88
+ const firstGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
89
+ const label = firstGroup.find('.stacked-bar-chart__groups__item__label')
90
+ expect(label.text()).toBe("Avatar")
91
+ })
92
+
93
+ it('creates the second group with "ET: The Extra-Terrestrial" as label', () => {
94
+ const firstGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(1)
95
+ const label = firstGroup.find('.stacked-bar-chart__groups__item__label')
96
+ expect(label.text()).toBe("ET: The Extra-Terrestrial")
97
+ })
98
+
99
+ it('creates the first group with "Avatar" as label when ordered by "budget"', async () => {
100
+ await wrapper.setProps({ sortBy: 'budget' })
101
+ const firstGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
102
+ const label = firstGroup.find('.stacked-bar-chart__groups__item__label')
103
+ expect(label.text()).toBe("ET: The Extra-Terrestrial")
104
+ })
105
+
106
+ it('creates a legend with "budget" and "box_office" items', () => {
107
+ const budgetLegend = wrapper.findAll('.stacked-bar-chart__legend__item').at(0)
108
+ const boxOfficeLegend = wrapper.findAll('.stacked-bar-chart__legend__item').at(1)
109
+ expect(budgetLegend.text()).toBe("budget")
110
+ expect(boxOfficeLegend.text()).toBe("box_office")
111
+ })
112
+
113
+ it('highlights the legend "budget"', async () => {
114
+ const budgetLegend = wrapper.findAll('.stacked-bar-chart__legend__item').at(0)
115
+ expect(budgetLegend.classes('stacked-bar-chart__legend__item--highlighted')).toBeFalsy()
116
+ await budgetLegend.trigger('mouseover')
117
+ await vi.advanceTimersByTimeAsync(wrapper.vm.highlightDelay)
118
+ expect(budgetLegend.classes('stacked-bar-chart__legend__item--highlighted')).toBeTruthy()
119
+ })
120
+
121
+ it('highlights the bars for "budget"', async () => {
122
+ const budgetLegend = wrapper.findAll('.stacked-bar-chart__legend__item').at(0)
123
+ await budgetLegend.trigger('mouseover')
124
+ await vi.advanceTimersByTimeAsync(wrapper.vm.highlightDelay)
125
+ const budgetBars = wrapper.findAll('.stacked-bar-chart__groups__item__bars__item--budget')
126
+ expect(budgetBars.at(0).classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
127
+ expect(budgetBars.at(1).classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
128
+ expect(budgetBars.at(2).classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
129
+ expect(budgetBars.at(3).classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
130
+ })
131
+
132
+ it('highlights the bars for "box_office"', async () => {
133
+ const boxOfficeLegend = wrapper.findAll('.stacked-bar-chart__legend__item').at(1)
134
+ await boxOfficeLegend.trigger('mouseover')
135
+ await vi.advanceTimersByTimeAsync(wrapper.vm.highlightDelay)
136
+ const budgetBars = wrapper.findAll('.stacked-bar-chart__groups__item__bars__item--box-office')
137
+ expect(budgetBars.at(0).classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
138
+ expect(budgetBars.at(1).classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
139
+ expect(budgetBars.at(2).classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
140
+ expect(budgetBars.at(3).classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
141
+ })
142
+
143
+ it('highlights the legend "budget" on mouseover and "box_office" by default', async () => {
144
+ await wrapper.setProps({ highlights: ['box_office'] })
145
+ const budgetLegend = wrapper.findAll('.stacked-bar-chart__legend__item').at(0)
146
+ const boxOfficeLegend = wrapper.findAll('.stacked-bar-chart__legend__item').at(1)
147
+ expect(budgetLegend.classes('stacked-bar-chart__legend__item--highlighted')).toBeFalsy()
148
+ expect(boxOfficeLegend.classes('stacked-bar-chart__legend__item--highlighted')).toBeTruthy()
149
+ await budgetLegend.trigger('mouseover')
150
+ await vi.advanceTimersByTimeAsync(wrapper.vm.highlightDelay)
151
+ expect(budgetLegend.classes('stacked-bar-chart__legend__item--highlighted')).toBeTruthy()
152
+ expect(boxOfficeLegend.classes('stacked-bar-chart__legend__item--highlighted')).toBeFalsy()
153
+ })
154
+
155
+ it('highlights the bars for "box_office" after a while', async () => {
156
+ const boxOfficeLegend = wrapper.findAll('.stacked-bar-chart__legend__item').at(1)
157
+ await boxOfficeLegend.trigger('mouseover')
158
+ expect(boxOfficeLegend.classes('stacked-bar-chart__legend__item--highlighted')).toBeFalsy()
159
+ await vi.advanceTimersByTimeAsync(wrapper.vm.highlightDelay / 2)
160
+ expect(boxOfficeLegend.classes('stacked-bar-chart__legend__item--highlighted')).toBeFalsy()
161
+ await vi.advanceTimersByTimeAsync(wrapper.vm.highlightDelay * 2)
162
+ expect(boxOfficeLegend.classes('stacked-bar-chart__legend__item--highlighted')).toBeTruthy()
163
+ })
164
+
165
+ it('highlights the whole "Avatar" row', async () => {
166
+ await wrapper.setProps({ rowHighlights: ['Avatar'] })
167
+ const boxOffice = wrapper.findAll('.stacked-bar-chart__groups__item__bars__item').at(0)
168
+ const budget = wrapper.findAll('.stacked-bar-chart__groups__item__bars__item').at(1)
169
+ expect(boxOffice.classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
170
+ expect(budget.classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
171
+ })
172
+
173
+ it('highlights the whole "Ghostbusters" row', async () => {
174
+ await wrapper.setProps({ rowHighlights: ['Ghostbusters'] })
175
+ const boxOffice = wrapper.findAll('.stacked-bar-chart__groups__item__bars__item').at(6)
176
+ const budget = wrapper.findAll('.stacked-bar-chart__groups__item__bars__item').at(7)
177
+ expect(boxOffice.classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
178
+ expect(budget.classes('stacked-bar-chart__groups__item__bars__item--highlighted')).toBeTruthy()
179
+ })
180
+
181
+ it('creates bars with specific colors', async () => {
182
+ await wrapper.setProps({ barColors: [ "#000", "#444" ] })
183
+ const firstGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
184
+ const budgetBar = firstGroup.find('.stacked-bar-chart__groups__item__bars__item--budget')
185
+ const boxOfficeBar = firstGroup.find('.stacked-bar-chart__groups__item__bars__item--box-office')
186
+ expect(budgetBar.element.style['background-color']).toBe('rgb(0, 0, 0)')
187
+ expect(boxOfficeBar.element.style['background-color']).toBe('rgb(68, 68, 68)')
188
+ })
189
+
190
+ it('creates legend with specific colors', async () => {
191
+ await wrapper.setProps({ barColors: [ "#000", "#444" ] })
192
+ const legendBoxes = wrapper.findAll('.stacked-bar-chart__legend__item__box')
193
+ const budgetBox = legendBoxes.at(0)
194
+ const boxOfficeBox = legendBoxes.at(1)
195
+ expect(budgetBox.element.style['background-color']).toBe('rgb(0, 0, 0)')
196
+ expect(boxOfficeBox.element.style['background-color']).toBe('rgb(68, 68, 68)')
197
+ })
198
+
199
+ it('creates one legend when using explicit keys', async () => {
200
+ await wrapper.setProps({ keys: [ "box_office" ] })
201
+ const legendItems = wrapper.findAll('.stacked-bar-chart__legend__item')
202
+ expect(legendItems).toHaveLength(1)
203
+ })
204
+
205
+ it('creates one bar when using explicit keys', async () => {
206
+ await wrapper.setProps({ keys: [ "box_office" ] })
207
+ const firstGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
208
+ const bars = firstGroup.findAll('.stacked-bar-chart__groups__item__bars__item')
209
+ expect(bars).toHaveLength(1)
210
+ })
211
+
212
+ it('creates legend with custom group names', async () => {
213
+ await wrapper.setProps({ groups: [ "Budget", "Box Office" ] })
214
+ const legendItems = wrapper.findAll('.stacked-bar-chart__legend__item')
215
+ expect(legendItems.at(0).text()).toBe("Budget")
216
+ expect(legendItems.at(1).text()).toBe("Box Office")
217
+ })
218
+
219
+ it('creates bar direct labeling without formatting', async () => {
220
+ const firstGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
221
+ const values = firstGroup.findAll('.stacked-bar-chart__groups__item__bars__item__value')
222
+ expect(values.at(0).text()).toBe('237')
223
+ expect(values.at(1).text()).toBe('2784')
224
+ })
225
+
226
+ it('creates bar direct labeling without with currency formatting', async () => {
227
+ await wrapper.setProps({ xAxisTickFormat: '$,' })
228
+ const firstGroup = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
229
+ const values = firstGroup.findAll('.stacked-bar-chart__groups__item__bars__item__value')
230
+ expect(values.at(0).text()).toBe('$237')
231
+ expect(values.at(1).text()).toBe('$2,784')
232
+ })
233
+ })
234
+
235
+ describe('a stacked-bars chart with 3 bars in 3 groups with empty values', () => {
236
+
237
+ let wrapper
238
+
239
+ beforeEach(async () => {
240
+
241
+ const propsData = {
242
+ labelField: 'label',
243
+ fixedHeight: 500,
244
+ hideEmptyValues: true,
245
+ highlightDelay: 0,
246
+ data: [
247
+ { label: 'today', foo: 90, 'baz 1': 5, 'baz 2': 5 },
248
+ { label: 'tomorrow', foo: 40, 'baz 1': 10, 'baz 2': 0 },
249
+ { label: 'next week', foo: 0, 'baz 1': 20, 'baz 2': 0 }
250
+ ]
251
+ }
252
+
253
+ const attrs = {
254
+ style: 'width: 600px'
255
+ }
256
+
257
+ wrapper = mount(StackedBarChart, { propsData, attrs })
258
+ })
259
+
260
+ it('is a Vue instance', () => {
261
+ expect(wrapper.vm).toBeTruthy()
262
+ })
263
+
264
+ it('creates 3 bars', async () => {
265
+ expect(wrapper.findAll('.stacked-bar-chart__groups__item')).toHaveLength(3)
266
+ })
267
+
268
+ it('creates the first bar with no hidden bars', () => {
269
+ const group = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
270
+ const hiddenBars = group.findAll('.stacked-bar-chart__groups__item__bars__item--hidden')
271
+ expect(hiddenBars).toHaveLength(0)
272
+ })
273
+
274
+ it('creates the first bar with normalized classes on bar', () => {
275
+ const group = wrapper.findAll('.stacked-bar-chart__groups__item').at(0)
276
+ const items = group.findAll('.stacked-bar-chart__groups__item__bars__item')
277
+ expect(items.at(0).classes('stacked-bar-chart__groups__item__bars__item--foo')).toBeTruthy()
278
+ expect(items.at(1).classes('stacked-bar-chart__groups__item__bars__item--baz-1')).toBeTruthy()
279
+ expect(items.at(2).classes('stacked-bar-chart__groups__item__bars__item--baz-2')).toBeTruthy()
280
+ })
281
+
282
+ it('creates the second bar with one hidden bar', () => {
283
+ const group = wrapper.findAll('.stacked-bar-chart__groups__item').at(1)
284
+ const hiddenBars = group.findAll('.stacked-bar-chart__groups__item__bars__item--hidden')
285
+ expect(hiddenBars).toHaveLength(1)
286
+ })
287
+
288
+ it('creates the third bar with two hidden bars', () => {
289
+ const group = wrapper.findAll('.stacked-bar-chart__groups__item').at(2)
290
+ const hiddenBars = group.findAll('.stacked-bar-chart__groups__item__bars__item--hidden')
291
+ expect(hiddenBars).toHaveLength(2)
292
+ })
293
+ })
294
+ })