@worksafevictoria/wcl7.5 1.1.1 → 1.1.3

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.
@@ -0,0 +1,61 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+
3
+ import * as ChartStories from './index.stories';
4
+
5
+ <Meta of={ChartStories} />
6
+ # Charts
7
+
8
+ Charts support links
9
+
10
+ * [vue-google-charts](https://github.com/devstark-com/vue-google-charts "Package") (package documentation)
11
+ * [Google-chart](https://developers.google.com/chart/ "Google charts") (customisation documentation)
12
+
13
+ <Canvas of={ChartStories.Default} />
14
+
15
+ ## Chart options
16
+
17
+ Visit Google-chart documentation for chart options.
18
+
19
+ ```tsx
20
+ options: {
21
+ title: 'graph',
22
+ pieSliceText: 'value',
23
+ is3D: true,
24
+ height: 600,
25
+ chartArea: { top: 20, bottom: 20 },
26
+ vAxis: { minValue: 0 },
27
+ animation: {
28
+ duration: 1000,
29
+ easing: 'out'
30
+ },
31
+ colors: [
32
+ '#3d3c36',
33
+ colors.orange100,
34
+ ]
35
+ }
36
+ ```
37
+
38
+ ## Chart filters
39
+
40
+ Filtering options. These are passed to 'directory-filters' component.
41
+
42
+ ```tsx
43
+ [
44
+ { record_id: 'DEATH_DATE', name: 'Year', date: true }, // Primary
45
+ { record_id: 'GENDER', name: 'Gender' }, // Secondary
46
+ { record_id: 'AGE_RANGE', name: 'Age range' },
47
+ { record_id: 'CATEGORY', name: 'Category' },
48
+ { record_id: 'INDUSTRY', name: 'Industry' },
49
+ { record_id: 'SIZE_', name: 'Size of Organisation' }
50
+ ]
51
+ ```
52
+
53
+ ## Filtering
54
+
55
+ Currently three filters are available for generating charts
56
+
57
+ **'primary':** Compare by Primary field (first in array)
58
+
59
+ **'months':** Incremental view (Requires date as Primary filter)
60
+
61
+ **'compare':** Compare Primary field with chosen field (default secondary)
@@ -0,0 +1,31 @@
1
+ import Chart from './index.vue'
2
+ import {
3
+ chart, filterFields, statsData
4
+ } from './Constants'
5
+ export default {
6
+ title: 'Paragraphs/Chart',
7
+ component: Chart,
8
+ }
9
+ export const Default = {
10
+ argTypes: {
11
+ "chart.type": {
12
+ control: "radio",
13
+ options: ["BarChart", "PieChart", "ColumnChart", "Table", "LineChart", "AreaChart", "BubbleChart", "ScatterChart"],
14
+ },
15
+ "chart.filter": {
16
+ control: "radio",
17
+ options: ["primary", "months", "compare"],
18
+ },
19
+ },
20
+ args: {
21
+ filterFields: filterFields,
22
+ chart: chart,
23
+ statsData: statsData
24
+ },
25
+ parameters: {
26
+ deepControls: { enabled: true },
27
+ },
28
+ }
29
+
30
+
31
+
@@ -0,0 +1,331 @@
1
+ <template>
2
+ <container>
3
+ <row>
4
+ <column class="pb-5">
5
+ <single-taxonomy
6
+ v-for="(t, i) in computedPropFilters"
7
+ :key="i"
8
+ :ref="`${t.record_id}`"
9
+ :data="t"
10
+ :results="filteredList.length"
11
+ :loading="isFiltering"
12
+ class="wcl-directory-filters__filterButton"
13
+ @onFilter="onFilter"
14
+ />
15
+ <filter-button
16
+ v-if="!hideReset"
17
+ :is-reset="true"
18
+ class="wcl-directory-filters__filterButton"
19
+ @clicked="reset"
20
+ />
21
+ </column>
22
+ </row>
23
+ <row>
24
+ <column>
25
+ <div>
26
+ <b-form-group
27
+ v-show="chart.filter === 'compare'"
28
+ label="Compare by:"
29
+ label-for="compare"
30
+ >
31
+ <b-form-select
32
+ id="compare"
33
+ v-model="graphSelected"
34
+ class="mb-1 mt-1"
35
+ :options="graphOptions"
36
+ ></b-form-select>
37
+ </b-form-group>
38
+ <g-chart
39
+ :type="filteredList.length > 0 ? chart.type : 'ColumnChart'"
40
+ :data="
41
+ filteredList.length > 0
42
+ ? chart.filter === 'primary'
43
+ ? by_primary()
44
+ : chart.filter === 'months'
45
+ ? by_months()
46
+ : compare()
47
+ : noData()
48
+ "
49
+ :options="chart.options"
50
+ />
51
+ </div>
52
+ </column>
53
+ </row>
54
+ </container>
55
+ </template>
56
+
57
+ <script>
58
+ import { GChart } from 'vue-google-charts'
59
+ import Container from '../../Containers/Container/index.vue'
60
+ import Column from '../../Containers/Column/index.vue'
61
+ import Row from '../../Containers/Row/index.vue'
62
+ import FilterButton from '../../Common/FilterButton/index.vue' // Reset
63
+ import SingleTaxonomy from '../../Global/DirectoryFilters/SingleTaxonomy/index.vue'
64
+ import { BFormGroup, BFormSelect } from 'bootstrap-vue-next'
65
+
66
+ export default {
67
+ name: 'Chart',
68
+ components: {
69
+ Row,
70
+ Column,
71
+ Container,
72
+ GChart,
73
+ FilterButton,
74
+ SingleTaxonomy,
75
+ BFormGroup,
76
+ BFormSelect
77
+ },
78
+ props: {
79
+ statsData: { type: Array, default: () => [] },
80
+ filterFields: { type: Array, default: () => [] },
81
+ chart: { type: Object, default: () => {} }
82
+ },
83
+ data() {
84
+ return {
85
+ hideReset: true,
86
+ isFiltering: false,
87
+ filters: {
88
+ taxonomies: [],
89
+ selected: {},
90
+ availableBundles: []
91
+ },
92
+ graphSelected: null,
93
+ chartHeight: this.chart.options.height ? this.chart.options.height : 600
94
+ }
95
+ },
96
+ computed: {
97
+ graphOptions: function() {
98
+ return this.filterFields.slice(1).map((field) => {
99
+ if (!this.graphSelected) {
100
+ this.graphSelected = field.record_id
101
+ }
102
+ return { value: field.record_id, text: field.name }
103
+ })
104
+ },
105
+ computedPropFilters: function() {
106
+ let fields = this.filterFields.map((field) => {
107
+ let terms = null
108
+ if (field.date) {
109
+ terms = this.statsData
110
+ .map((item) => item[field.record_id].split('/')[2]) // Date field get year
111
+ .filter((value, index, self) => self.indexOf(value) === index)
112
+ .map((value) => {
113
+ return { tid: value, name: value, preselected: false }
114
+ })
115
+ .sort((a, b) => a.name.localeCompare(b.name))
116
+ } else {
117
+ terms = this.statsData
118
+ .map((item) => item[field.record_id])
119
+ .filter((value, index, self) => self.indexOf(value) === index)
120
+ .map((value) => {
121
+ return { tid: value, name: value, preselected: false }
122
+ })
123
+ .sort((a, b) => a.name.localeCompare(b.name))
124
+ }
125
+ return {
126
+ name: field.name,
127
+ record_id: field.record_id,
128
+ terms
129
+ }
130
+ })
131
+ return fields
132
+ },
133
+ filteredList: function() {
134
+ let filterKeys = Object.keys(this.filters.selected).map((key) => key)
135
+ let list = this.statsData.filter((element) => {
136
+ let include = true
137
+ for (var i = 0, n = filterKeys.length; i < n; ++i) {
138
+ if (this.filters.selected[filterKeys[i]].length > 0) {
139
+ let filterType = this.filterFields.find(
140
+ (item) => item.record_id === filterKeys[i]
141
+ )
142
+ if (filterType.date) {
143
+ // Filter by year
144
+ if (
145
+ this.filters.selected[filterKeys[i]].indexOf(
146
+ element[filterKeys[i]].split('/')[2]
147
+ ) === -1
148
+ ) {
149
+ include = false
150
+ }
151
+ } else {
152
+ if (
153
+ this.filters.selected[filterKeys[i]].indexOf(
154
+ element[filterKeys[i]]
155
+ ) === -1
156
+ ) {
157
+ include = false
158
+ }
159
+ }
160
+ }
161
+ }
162
+ if (include) {
163
+ return element
164
+ }
165
+ })
166
+ return list
167
+ }
168
+ },
169
+ methods: {
170
+ onFilter(filters) {
171
+ this.hideReset = false
172
+ const allFiltersUnselected = Object.values(this.$refs).every(
173
+ (taxonomy) => {
174
+ return Object.values(taxonomy[0].selectedFilters).every(
175
+ (filter) => filter.filter((item) => !item.preselected).length === 0
176
+ )
177
+ }
178
+ )
179
+ if (allFiltersUnselected) {
180
+ this.hideReset = true
181
+ }
182
+ Object.keys(filters).forEach((filter) => {
183
+ this.filters.selected[filter] = filters[filter].map((obj) => obj.tid)
184
+ })
185
+ },
186
+ reset() {
187
+ this.hideReset = true
188
+ Object.keys(this.$refs).forEach((key) => {
189
+ this.$refs[key][0].reset()
190
+ })
191
+ this.filters.selected = {}
192
+ },
193
+ by_primary() {
194
+ let primaryFilter = this.filterFields[0]
195
+ let data = [[primaryFilter.name, this.chart.header]]
196
+ let keys = {}
197
+
198
+ this.filteredList.forEach((element) => {
199
+ // Work off primary field
200
+ let group = null
201
+ if (primaryFilter.date) {
202
+ group = `${element[primaryFilter.record_id].split('/')[2]} `
203
+ } else {
204
+ group = element[primaryFilter.record_id]
205
+ }
206
+
207
+ if (group in keys) {
208
+ keys[group]++
209
+ } else {
210
+ keys[group] = 1
211
+ }
212
+ })
213
+ Object.keys(keys).forEach((item) => {
214
+ data.push([item, keys[item]])
215
+ })
216
+ if (this.chart.type === 'BarChart') {
217
+ this.chart.options.height = data[0].length * 200
218
+ } else {
219
+ this.chart.options.height = this.chartHeight
220
+ }
221
+ return data
222
+ },
223
+ by_months() {
224
+ let primaryFilter = this.filterFields[0]
225
+ if (!primaryFilter.date) {
226
+ return ['Requires date in primary field']
227
+ }
228
+ let data = [
229
+ ['Month'],
230
+ ['Jan'],
231
+ ['Feb'],
232
+ ['Mar'],
233
+ ['Apr'],
234
+ ['May'],
235
+ ['Jun'],
236
+ ['Jul'],
237
+ ['Aug'],
238
+ ['Sep'],
239
+ ['Oct'],
240
+ ['Nov'],
241
+ ['Dec']
242
+ ]
243
+ let years = this.filteredList
244
+ .map((item) => item[primaryFilter.record_id].split('/')[2])
245
+ .filter((value, index, self) => self.indexOf(value) === index)
246
+ data[0].push(...years)
247
+ for (var i = 1, n = data.length; i < n; ++i) {
248
+ for (var j = 0, m = years.length; j < m; ++j) {
249
+ data[i].push(0)
250
+ }
251
+ }
252
+ this.filteredList.forEach((element) => {
253
+ let year = element[primaryFilter.record_id].split('/')[2]
254
+ let month = element[primaryFilter.record_id].split('/')[1]
255
+ data[month][data[0].indexOf(year)]++
256
+ })
257
+ for (var i = 2; i < data.length; i++) {
258
+ for (var j = 1; j < data[i].length; j++) {
259
+ data[i][j] = data[i][j] + data[i - 1][j]
260
+ }
261
+ }
262
+ if (this.chart.type === 'BarChart') {
263
+ this.chart.options.height = data[0].length * 200
264
+ } else {
265
+ this.chart.options.height = this.chartHeight
266
+ }
267
+ return data
268
+ },
269
+ compare() {
270
+ let primaryFilter = this.filterFields[0]
271
+ let comparison = this.filteredList
272
+ .map((item) => item[this.graphSelected])
273
+ .filter((value, index, self) => self.indexOf(value) === index)
274
+ .sort((a, b) => a.localeCompare(b))
275
+ let data = [['Year', ...comparison]]
276
+ let keys = {}
277
+ this.filteredList.forEach((element) => {
278
+ let group = null
279
+ if (primaryFilter.date) {
280
+ group = `${element[primaryFilter.record_id].split('/')[2]} `
281
+ } else {
282
+ group = element[primaryFilter.record_id]
283
+ }
284
+ if (group in keys) {
285
+ } else {
286
+ keys[group] = {}
287
+ comparison.forEach((co) => {
288
+ keys[group][co] = 0
289
+ })
290
+ }
291
+ keys[group][element[this.graphSelected]]++
292
+ })
293
+ Object.keys(keys).forEach((key) => {
294
+ data.push([key, ...Object.entries(keys[key]).map(([k, v]) => v)])
295
+ })
296
+ if (this.chart.type === 'BarChart') {
297
+ this.chart.options.height = data[0].length * 200
298
+ } else {
299
+ this.chart.options.height = this.chartHeight
300
+ }
301
+ return data
302
+ },
303
+ noData() {
304
+ return [
305
+ [
306
+ { label: 'Results', type: 'string' },
307
+ { label: 'Results', type: 'number' },
308
+ { role: 'style', type: 'string' },
309
+ { role: 'annotation', type: 'string' }
310
+ ],
311
+ ['', 0, null, 'No results found']
312
+ ]
313
+ }
314
+ }
315
+ }
316
+ </script>
317
+
318
+ <style lang="scss" scoped>
319
+ @import '../../../includes/scss/all';
320
+
321
+ .wcl-directory-filters {
322
+ &__filterButton {
323
+ display: inline-block;
324
+ padding-right: 16px;
325
+ line-height: 3;
326
+ &:last-child {
327
+ padding-right: 0px;
328
+ }
329
+ }
330
+ }
331
+ </style>
@@ -20,6 +20,21 @@ $yellow: #ffd229;
20
20
  $lightyellow: #fff6d4;
21
21
  $outline: #da47ff;
22
22
  $outline-dark: #ffffff;
23
+ // Functional colours
24
+ $wsv-fun-dark-green: #576D2F;
25
+ $wsv-fun-dark-blue: #104f77;
26
+ $wsv-fun-dark-purple: #55356b;
27
+ $wsv-fun-dark-red: #760031;
28
+ $wsv-fun-dark-brown: #763b00;
29
+ $wsv-fun-dark-caramel: #cb7c2d;
30
+ $wsv-fun-light-teal: #7ac2b5;
31
+ $wsv-fun-light-blue: #8ecade;
32
+ $wsv-fun-light-purple: #9871a8;
33
+ $wsv-fun-light-fuchsia: #f16a7e;
34
+ $wsv-fun-light-green: #edeb72;
35
+ $wsv-fun-light-brown: #dbc38b;
36
+
37
+
23
38
 
24
39
  $theme-colors: (
25
40
  'gold': $gold,
@@ -43,7 +58,20 @@ $theme-colors: (
43
58
  'yellow': $yellow,
44
59
  'lightyellow': $lightyellow,
45
60
  'outline': $outline,
46
- 'outline-dark': $outline-dark
61
+ 'outline-dark': $outline-dark,
62
+ // Functional colours
63
+ 'wsv-fun-dark-green':$wsv-fun-dark-green,
64
+ 'wsv-fun-dark-blue':$wsv-fun-dark-blue,
65
+ 'wsv-fun-dark-purple':$wsv-fun-dark-purple,
66
+ 'wsv-fun-dark-red':$wsv-fun-dark-red,
67
+ 'wsv-fun-dark-brown':$wsv-fun-dark-brown,
68
+ 'wsv-fun-dark-caramel':$wsv-fun-dark-caramel,
69
+ 'wsv-fun-light-teal':$wsv-fun-light-teal,
70
+ 'wsv-fun-light-blue':$wsv-fun-light-blue,
71
+ 'wsv-fun-light-purple':$wsv-fun-light-purple,
72
+ 'wsv-fun-light-fuchsia':$wsv-fun-light-fuchsia,
73
+ 'wsv-fun-light-green':$wsv-fun-light-green,
74
+ 'wsv-fun-light-brown':$wsv-fun-light-brown
47
75
  );
48
76
 
49
77
  :export {
package/src/index.js CHANGED
@@ -1,17 +1,17 @@
1
1
  // Global Components
2
- import Container from './components/Containers/Container'
3
- import Row from './components/Containers/Row'
4
- import Column from './components/Containers/Column'
5
- import HomepageHeader from './components/Containers/HomepageHeader'
6
- import Subheader from './components/Containers/Subheader'
7
- import SectionGroup from './components/Containers/SectionGroup'
2
+ import Container from './components/Containers/Container/index.vue'
3
+ import Row from './components/Containers/Row/index.vue'
4
+ import Column from './components/Containers/Column/index.vue'
5
+ import HomepageHeader from './components/Containers/HomepageHeader/index.vue'
6
+ import Subheader from './components/Containers/Subheader/index.vue'
7
+ import SectionGroup from './components/Containers/SectionGroup/index.vue'
8
8
 
9
- import AppHeader from './components/Global/AppHeader'
10
- import AppFooter from './components/Global/AppFooter'
11
- import HeroHeader from './components/Global/HeroHeader'
12
- import SocialShare from './components/Global/SocialShare'
13
- import Strip from './components/Global/Strip'
14
- import AlertStrip from './components/Global/AlertStrip'
9
+ import AppHeader from './components/Global/AppHeader/index.vue'
10
+ import AppFooter from './components/Global/AppFooter/index.vue'
11
+ import HeroHeader from './components/Global/HeroHeader/index.vue'
12
+ import SocialShare from './components/Global/SocialShare/index.vue'
13
+ import Strip from './components/Global/Strip/index.vue'
14
+ import AlertStrip from './components/Global/AlertStrip/index.vue'
15
15
  import CardGrid from './components/Common/CardGrid/index.vue'
16
16
  import CardGridItem from './components/Common/CardGridItem/index.vue'
17
17
  import Cookies from './components/Global/Cookies/index.vue'
@@ -20,35 +20,36 @@ import GlobalNotice from './components/Global/GlobalNotice/index.vue'
20
20
  import ContrastMode from './components/Global/ContrastMode/index.vue'
21
21
 
22
22
  // Paragraphs
23
- import RichText from './components/Paragraphs/RichText'
24
- import TextMedia from './components/Paragraphs/TextMedia'
25
- import Accordion from './components/Paragraphs/Accordion'
26
- import TabbedCards from './components/Paragraphs/TabbedCards'
27
- import BrowseContent from './components/Paragraphs/BrowseContent'
28
- import TaskFinder from './components/Paragraphs/TaskFinder'
29
- import ListGroup from './components/Paragraphs/ListGroup'
30
- import ScrollSpy from './components/Paragraphs/ScrollSpy'
31
- import Directory from './components/Paragraphs/Directory'
32
- import SelectableCards from './components/Paragraphs/SelectableCards'
33
- import VideoMedia from './components/Paragraphs/VideoPlayer'
34
- import VideoGrid from './components/Paragraphs/VideoGrid'
35
- import Statistics from './components/Paragraphs/Statistics'
36
- import ProofPoints from './components/Paragraphs/ProofPoints'
37
- import TabulatedData from './components/Paragraphs/TabulatedData'
38
- import Breakout from './components/Paragraphs/Breakout'
39
- import Tabs from './components/Paragraphs/Tabs'
40
- import MarketingBanner from './components/Paragraphs/MarketingBanner'
41
- import RelatedInformation from './components/Paragraphs/RelatedInformation'
42
- import Calculator from './components/Paragraphs/Calculator'
23
+ import RichText from './components/Paragraphs/RichText/index.vue'
24
+ import TextMedia from './components/Paragraphs/TextMedia/index.vue'
25
+ import Accordion from './components/Paragraphs/Accordion/index.vue'
26
+ import TabbedCards from './components/Paragraphs/TabbedCards/index.vue'
27
+ import BrowseContent from './components/Paragraphs/BrowseContent/index.vue'
28
+ import TaskFinder from './components/Paragraphs/TaskFinder/index.vue'
29
+ import ListGroup from './components/Paragraphs/ListGroup/index.vue'
30
+ import ScrollSpy from './components/Paragraphs/ScrollSpy/index.vue'
31
+ import Directory from './components/Paragraphs/Directory/index.vue'
32
+ import SelectableCards from './components/Paragraphs/SelectableCards/index.vue'
33
+ import VideoMedia from './components/Paragraphs/VideoPlayer/index.vue'
34
+ import VideoGrid from './components/Paragraphs/VideoGrid/index.vue'
35
+ import Statistics from './components/Paragraphs/Statistics/index.vue'
36
+ import ProofPoints from './components/Paragraphs/ProofPoints/index.vue'
37
+ import TabulatedData from './components/Paragraphs/TabulatedData/index.vue'
38
+ import Breakout from './components/Paragraphs/Breakout/index.vue'
39
+ import Tabs from './components/Paragraphs/Tabs/index.vue'
40
+ import MarketingBanner from './components/Paragraphs/MarketingBanner/index.vue'
41
+ import RelatedInformation from './components/Paragraphs/RelatedInformation/index.vue'
42
+ import Calculator from './components/Paragraphs/Calculator/index.vue'
43
+ import Chart from './components/Paragraphs/Chart/index.vue'
43
44
 
44
45
  // SubComponents
45
- import FormAddressPostcode from './components/SubComponents/FormAddressPostcode'
46
- import CtaButton from './components/SubComponents/CtaButton'
47
- import Pagination from './components/SubComponents/Pagination'
48
- import SingleImage from './components/SubComponents/SingleImage'
49
- import Loading from './components/SubComponents/Loading'
50
- import Icon from './components/SubComponents/Icon'
51
- import Breadcrumb from './components/SubComponents/Breadcrumb'
46
+ import FormAddressPostcode from './components/SubComponents/FormAddressPostcode/index.vue'
47
+ import CtaButton from './components/SubComponents/CtaButton/index.vue'
48
+ import Pagination from './components/SubComponents/Pagination/index.vue'
49
+ import SingleImage from './components/SubComponents/SingleImage/index.vue'
50
+ import Loading from './components/SubComponents/Loading/index.vue'
51
+ import Icon from './components/SubComponents/Icon/index.vue'
52
+ import Breadcrumb from './components/SubComponents/Breadcrumb/index.vue'
52
53
  import CardGroup from './components/SubComponents/CardGroup/index.vue'
53
54
  import Search from './components/SubComponents/Search/index.vue'
54
55
  import VideoThumbnail from './components/SubComponents/VideoThumbnail/index.vue'
@@ -98,7 +99,8 @@ export {
98
99
  Tabs,
99
100
  MarketingBanner,
100
101
  RelatedInformation,
101
- Calculator
102
+ Calculator,
103
+ Chart
102
104
  }
103
105
 
104
106
  // Export Sub Components