@cdc/core 4.23.11 → 4.24.2

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 (57) hide show
  1. package/components/DataTable/DataTable.tsx +39 -10
  2. package/components/DataTable/components/ChartHeader.tsx +17 -5
  3. package/components/DataTable/components/ExpandCollapse.tsx +1 -1
  4. package/components/DataTable/helpers/chartCellMatrix.tsx +16 -2
  5. package/components/DataTable/helpers/customColumns.ts +25 -0
  6. package/components/DataTable/helpers/customSort.ts +9 -0
  7. package/components/DataTable/helpers/getChartCellValue.ts +2 -1
  8. package/components/DataTable/helpers/getDataSeriesColumns.ts +2 -1
  9. package/components/DataTable/helpers/getSeriesName.ts +15 -20
  10. package/components/DataTable/helpers/mapCellMatrix.tsx +4 -0
  11. package/components/DataTable/types/TableConfig.ts +11 -40
  12. package/components/EditorPanel/DataTableEditor.tsx +133 -0
  13. package/components/EditorPanel/Inputs.tsx +150 -0
  14. package/components/Filters.jsx +17 -15
  15. package/components/MediaControls.jsx +1 -1
  16. package/components/MultiSelect/MultiSelect.tsx +95 -0
  17. package/components/MultiSelect/index.ts +1 -0
  18. package/components/MultiSelect/multiselect.styles.css +50 -0
  19. package/components/Table/Table.tsx +23 -3
  20. package/components/Table/components/Cell.tsx +3 -3
  21. package/components/Table/components/GroupRow.tsx +6 -2
  22. package/components/Table/components/Row.tsx +9 -2
  23. package/components/Table/types/RowType.ts +5 -0
  24. package/components/_stories/DataTable.stories.tsx +41 -0
  25. package/components/_stories/EditorPanel.stories.tsx +53 -0
  26. package/components/_stories/Inputs.stories.tsx +37 -0
  27. package/components/_stories/MultiSelect.stories.tsx +24 -0
  28. package/components/_stories/_mocks/row_type.json +42 -0
  29. package/components/inputs/{InputSelect.jsx → InputSelect.tsx} +15 -5
  30. package/components/managers/DataDesigner.tsx +8 -8
  31. package/components/ui/{Icon.jsx → Icon.tsx} +3 -3
  32. package/components/ui/_stories/Colors.stories.tsx +92 -0
  33. package/components/ui/_stories/Icon.stories.tsx +17 -10
  34. package/helpers/DataTransform.ts +30 -2
  35. package/helpers/fetchRemoteData.js +5 -5
  36. package/helpers/getFileExtension.ts +28 -5
  37. package/helpers/getViewport.ts +23 -0
  38. package/helpers/isSolr.js +13 -0
  39. package/helpers/withDevTools.ts +50 -0
  40. package/package.json +2 -2
  41. package/styles/_data-table.scss +2 -5
  42. package/styles/_global-variables.scss +75 -0
  43. package/styles/base.scss +89 -69
  44. package/types/Action.ts +1 -0
  45. package/types/Axis.ts +39 -2
  46. package/types/BoxPlot.ts +21 -0
  47. package/types/Column.ts +16 -0
  48. package/types/FilterBehavior.ts +1 -0
  49. package/types/General.ts +9 -0
  50. package/types/Runtime.ts +21 -1
  51. package/types/Series.ts +1 -1
  52. package/types/Table.ts +21 -0
  53. package/types/UpdateFieldFunc.ts +1 -0
  54. package/types/ViewPort.ts +2 -0
  55. package/types/Visualization.ts +15 -9
  56. package/types/WCMSProps.ts +11 -0
  57. package/helpers/getViewport.js +0 -21
@@ -0,0 +1,150 @@
1
+ import { memo, useEffect, useState } from 'react'
2
+ import { useDebounce } from 'use-debounce'
3
+
4
+ export type Input = {
5
+ label: string
6
+ tooltip?: any
7
+ section?: any
8
+ placeholder?: string
9
+ subsection?: any
10
+ updateField?: Function
11
+ fieldName?: string
12
+ }
13
+
14
+ export type TextFieldProps = {
15
+ className?: string
16
+ value: string | number
17
+ type?: 'text' | 'number' | 'textarea' | 'date'
18
+ min?: number
19
+ max?: number
20
+ i?: number
21
+ id?: string
22
+ } & Input
23
+
24
+ export type CheckboxProps = {
25
+ value?: boolean
26
+ min?: number
27
+ i?: number
28
+ className?: string
29
+ } & Input
30
+
31
+ export type SelectProps = {
32
+ value?: string
33
+ options?: string[]
34
+ required?: boolean
35
+ initial?: string
36
+
37
+ // all other props
38
+ [x: string]: any
39
+ } & Input
40
+
41
+ const TextField = memo((props: TextFieldProps) => {
42
+ const { label, tooltip, section = null, subsection = null, fieldName, updateField, value: stateValue, type = 'text', i = null, min = null, ...attributes } = props
43
+ const [value, setValue] = useState(stateValue)
44
+ const [debouncedValue] = useDebounce(value, 500)
45
+
46
+ useEffect(() => {
47
+ if ('string' === typeof debouncedValue && stateValue !== debouncedValue) {
48
+ updateField(section, subsection, fieldName, debouncedValue, i)
49
+ }
50
+ }, [debouncedValue])
51
+
52
+ let name = subsection ? `${section}-${subsection}-${fieldName}` : `${section}-${subsection}-${fieldName}`
53
+
54
+ const onChange = e => {
55
+ if ('number' !== type || min === null) {
56
+ setValue(e.target.value)
57
+ } else {
58
+ if (!e.target.value || min <= parseFloat(e.target.value)) {
59
+ setValue(e.target.value)
60
+ } else {
61
+ setValue(min.toString())
62
+ }
63
+ }
64
+ }
65
+
66
+ let formElement = <input type='text' name={name} onChange={onChange} {...attributes} value={value} />
67
+
68
+ if ('textarea' === type) {
69
+ formElement = <textarea name={name} onChange={onChange} {...attributes} value={value}></textarea>
70
+ }
71
+
72
+ if ('number' === type) {
73
+ formElement = <input type='number' name={name} onChange={onChange} {...attributes} value={value} />
74
+ }
75
+
76
+ if ('date' === type) {
77
+ formElement = <input type='date' name={name} onChange={onChange} {...attributes} value={value} />
78
+ }
79
+
80
+ return (
81
+ <label>
82
+ <span className='edit-label column-heading'>
83
+ {label}
84
+ {tooltip}
85
+ </span>
86
+ {formElement}
87
+ </label>
88
+ )
89
+ })
90
+
91
+ const CheckBox = memo((props: CheckboxProps) => {
92
+ const { label, value, fieldName, section = null, subsection = null, tooltip, updateField, ...attributes } = props
93
+
94
+ return (
95
+ <label className='checkbox column-heading'>
96
+ <input
97
+ type='checkbox'
98
+ name={fieldName}
99
+ checked={value}
100
+ onChange={e => {
101
+ updateField(section, subsection, fieldName, !value)
102
+ }}
103
+ {...attributes}
104
+ />
105
+ <span className='edit-label'>
106
+ {label}
107
+ {tooltip}
108
+ </span>
109
+ </label>
110
+ )
111
+ })
112
+
113
+ const Select = memo((props: SelectProps) => {
114
+ const { label, value, options, fieldName, section = null, subsection = null, required = false, tooltip, updateField, initial: initialValue, ...attributes } = props
115
+ let optionsJsx = options.map((optionName, index) => (
116
+ <option value={optionName} key={index}>
117
+ {optionName}
118
+ </option>
119
+ ))
120
+
121
+ if (initialValue) {
122
+ optionsJsx.unshift(
123
+ <option value='' key='initial'>
124
+ {initialValue}
125
+ </option>
126
+ )
127
+ }
128
+
129
+ return (
130
+ <label>
131
+ <span className='edit-label'>
132
+ {label}
133
+ {tooltip}
134
+ </span>
135
+ <select
136
+ className={required && !value ? 'warning' : ''}
137
+ name={fieldName}
138
+ value={value}
139
+ onChange={event => {
140
+ updateField(section, subsection, fieldName, event.target.value)
141
+ }}
142
+ {...attributes}
143
+ >
144
+ {optionsJsx}
145
+ </select>
146
+ </label>
147
+ )
148
+ })
149
+
150
+ export { Select, CheckBox, TextField }
@@ -74,22 +74,17 @@ export const useFilters = props => {
74
74
  const changeFilterActive = (index, value) => {
75
75
  let newFilters = visualizationConfig.type === 'map' ? [...filteredData] : [...visualizationConfig.filters]
76
76
 
77
- newFilters[index].active = value
78
- setConfig({ ...visualizationConfig })
79
-
80
- // If this is a button filter type show the button.
81
77
  if (visualizationConfig.filterBehavior === 'Apply Button') {
78
+ newFilters[index].queuedActive = value
82
79
  setShowApplyButton(true)
80
+ } else {
81
+ newFilters[index].active = value
83
82
  }
84
-
85
- // If we're not using the apply button we can set the filters right away.
86
- if (visualizationConfig.filterBehavior !== 'Apply Button') {
87
- setConfig({
88
- ...visualizationConfig,
89
- filters: newFilters
90
- })
91
- }
92
-
83
+ setConfig({
84
+ ...visualizationConfig,
85
+ filters: newFilters
86
+ })
87
+
93
88
  // Used for setting active filter, fromHash breaks the filteredData functionality.
94
89
  if (visualizationConfig.type === 'map' && visualizationConfig.filterBehavior === 'Filter Change') {
95
90
  setFilteredData(newFilters)
@@ -102,6 +97,13 @@ export const useFilters = props => {
102
97
  }
103
98
 
104
99
  const handleApplyButton = newFilters => {
100
+ newFilters.forEach(newFilter => {
101
+ if(newFilter.queuedActive){
102
+ newFilter.active = newFilter.queuedActive
103
+ delete newFilter.queuedActive
104
+ }
105
+ })
106
+
105
107
  setConfig({ ...visualizationConfig, filters: newFilters })
106
108
 
107
109
  if (type === 'map') {
@@ -320,7 +322,7 @@ const Filters = props => {
320
322
  const tabValues = []
321
323
  const tabBarValues = []
322
324
 
323
- const { active, label, filterStyle } = singleFilter
325
+ const { active, queuedActive, label, filterStyle } = singleFilter
324
326
 
325
327
  handleSorting(singleFilter)
326
328
 
@@ -387,7 +389,7 @@ const Filters = props => {
387
389
  {filterStyle === 'tab' && !mobileFilterStyle && <Filters.Tabs tabs={tabValues} />}
388
390
  {filterStyle === 'pill' && !mobileFilterStyle && <Filters.Pills pills={pillValues} />}
389
391
  {filterStyle === 'tab bar' && !mobileFilterStyle && <Filters.TabBar filter={singleFilter} index={outerIndex} />}
390
- {(filterStyle === 'dropdown' || mobileFilterStyle) && <Filters.Dropdown filter={singleFilter} index={outerIndex} label={label} active={active} filters={values} />}
392
+ {(filterStyle === 'dropdown' || mobileFilterStyle) && <Filters.Dropdown filter={singleFilter} index={outerIndex} label={label} active={queuedActive || active} filters={values} />}
391
393
  </>
392
394
  </div>
393
395
  )
@@ -59,7 +59,7 @@ const generateMedia = (state, type, elementToCapture) => {
59
59
 
60
60
  switch (type) {
61
61
  case 'image':
62
- html2canvas(baseSvg, {foreignObjectRendering: true}).then(canvas => {
62
+ html2canvas(baseSvg, {foreignObjectRendering: true, x: -1 * (window.pageXOffset + baseSvg.getBoundingClientRect().left), y: -1 * (window.pageYOffset + baseSvg.getBoundingClientRect().top)}).then(canvas => {
63
63
  saveImageAs(canvas.toDataURL(), filename + '.png')
64
64
  })
65
65
  return
@@ -0,0 +1,95 @@
1
+ import React, { useEffect, useRef, useState } from 'react'
2
+ import Icon from '../ui/Icon'
3
+
4
+ import './multiselect.styles.css'
5
+ import { UpdateFieldFunc } from '../../types/UpdateFieldFunc'
6
+
7
+ interface Option {
8
+ value: string
9
+ label: string
10
+ }
11
+
12
+ interface MultiSelectProps {
13
+ section?: string
14
+ subsection?: string
15
+ fieldName: string
16
+ options: Option[]
17
+ updateField: UpdateFieldFunc<string[]>
18
+ label?: string
19
+ }
20
+
21
+ const MultiSelect: React.FC<MultiSelectProps> = ({ section = null, subsection = null, fieldName, label, options, updateField }) => {
22
+ const [selectedItems, setSelectedItems] = useState<Option[]>([])
23
+ const [expanded, setExpanded] = useState(false)
24
+ const multiSelectRef = useRef(null)
25
+
26
+ useEffect(() => {
27
+ const handleClickOutside = event => {
28
+ if (multiSelectRef.current && !multiSelectRef.current.contains(event.target)) {
29
+ setExpanded(false)
30
+ }
31
+ }
32
+
33
+ document.addEventListener('mousedown', handleClickOutside)
34
+
35
+ return () => {
36
+ document.removeEventListener('mousedown', handleClickOutside)
37
+ }
38
+ }, [])
39
+
40
+ const update = newItems =>
41
+ updateField(
42
+ section,
43
+ subsection,
44
+ fieldName,
45
+ newItems.map(item => item.value)
46
+ )
47
+
48
+ const handleItemSelect = (option: Option) => {
49
+ const newItems = [...selectedItems, option]
50
+ setSelectedItems(newItems)
51
+ update(newItems)
52
+ }
53
+
54
+ const handleItemRemove = (option: Option) => {
55
+ const newItems = selectedItems.filter(item => item.value !== option.value)
56
+ setSelectedItems(newItems)
57
+ update(newItems)
58
+ }
59
+
60
+ const multiID = 'multiSelect_' + label
61
+ return (
62
+ <div ref={multiSelectRef} className='cove-multiselect'>
63
+ {label && (
64
+ <span id={multiID} className='edit-label cove-input__label'>
65
+ {label}
66
+ </span>
67
+ )}
68
+
69
+ <div aria-labelledby={label ? multiID : undefined} className='selected'>
70
+ {selectedItems.map(item => (
71
+ <div key={item.value} role='button' tabIndex={0} onClick={() => handleItemRemove(item)} onKeyUp={() => handleItemRemove(item)}>
72
+ {item.label}
73
+ <button aria-label='Remove' onClick={() => handleItemRemove(item)}>
74
+ x
75
+ </button>
76
+ </div>
77
+ ))}
78
+ <button aria-label={expanded ? 'Collapse' : 'Expand'} className='expand' onClick={() => setExpanded(!expanded)}>
79
+ <Icon display={expanded ? 'caretDown' : 'caretUp'} style={{ cursor: 'pointer' }} />
80
+ </button>
81
+ </div>
82
+ <ul className={'dropdown' + (expanded ? '' : ' hide')}>
83
+ {options
84
+ .filter(option => !selectedItems.find(item => item.value === option.value))
85
+ .map(option => (
86
+ <li className='cove-multiselect-li' key={option.value} role='option' tabIndex={0} onClick={() => handleItemSelect(option)} onKeyUp={() => handleItemSelect(option)}>
87
+ {option.label}
88
+ </li>
89
+ ))}
90
+ </ul>
91
+ </div>
92
+ )
93
+ }
94
+
95
+ export default MultiSelect
@@ -0,0 +1 @@
1
+ export { default } from './MultiSelect'
@@ -0,0 +1,50 @@
1
+ .cove-multiselect {
2
+ position: relative;
3
+ .selected {
4
+ border: 1px solid #ccc;
5
+ padding: 5px;
6
+ min-height: 40px;
7
+ :is(button) {
8
+ border: none;
9
+ background: none;
10
+ }
11
+ :is(div) {
12
+ display: inline-block;
13
+ padding: 0 0 0 5px;
14
+ margin-right: 5px;
15
+ margin-bottom: 2px;
16
+ background: #ccc;
17
+ border-radius: 5px;
18
+ }
19
+ .expand {
20
+ padding: 0 5px;
21
+ border-radius: 5px;
22
+ background: #ccc;
23
+ float: right;
24
+ }
25
+ border-radius: 5px;
26
+ }
27
+ .dropdown {
28
+ background: white;
29
+ position: absolute;
30
+ margin-top: 5px;
31
+ border: 1px solid #ccc;
32
+ padding: 0;
33
+ min-height: 40px;
34
+ overflow: scroll;
35
+ max-height: 200px;
36
+ z-index: 1;
37
+ &.hide {
38
+ display: none;
39
+ }
40
+
41
+ :is(li) {
42
+ cursor: pointer;
43
+ list-style: none;
44
+ padding-left: 10px;
45
+ &:hover {
46
+ background: #ccc;
47
+ }
48
+ }
49
+ }
50
+ }
@@ -2,6 +2,7 @@ import { ReactNode } from 'react'
2
2
  import Row from './components/Row'
3
3
  import GroupRow from './components/GroupRow'
4
4
  import { CellMatrix, GroupCellMatrix } from './types/CellMatrix'
5
+ import { RowType } from './types/RowType'
5
6
 
6
7
  type TableProps = {
7
8
  childrenMatrix: CellMatrix | GroupCellMatrix
@@ -14,12 +15,15 @@ type TableProps = {
14
15
  'aria-live'?: 'off' | 'assertive' | 'polite'
15
16
  hidden?: boolean
16
17
  'aria-rowcount'?: number
18
+ cellMinWidth?: number
17
19
  }
20
+ wrapColumns?: boolean
21
+ hasRowType?: boolean // if it has row type then the first column is the row type which will explain how to render the row
18
22
  }
19
23
 
20
24
  type Position = 'sticky'
21
25
 
22
- const Table = ({ childrenMatrix, tableName, caption, stickyHeader, headContent, tableOptions }: TableProps) => {
26
+ const Table = ({ childrenMatrix, tableName, caption, stickyHeader, headContent, tableOptions, wrapColumns, hasRowType }: TableProps) => {
23
27
  const headStyle = stickyHeader ? { position: 'sticky' as Position, top: 0, zIndex: 999 } : {}
24
28
  const isGroupedMatrix = !Array.isArray(childrenMatrix)
25
29
  return (
@@ -33,13 +37,29 @@ const Table = ({ childrenMatrix, tableName, caption, stickyHeader, headContent,
33
37
  const rows = childrenMatrix[groupName].map((row, i) => {
34
38
  colSpan = row.length
35
39
  const key = `${tableName}-${groupName}-row-${i}`
36
- return <Row key={key} rowKey={key} childRow={row} />
40
+ return <Row key={key} rowKey={key} childRow={row} wrapColumns={wrapColumns} cellMinWidth={tableOptions.cellMinWidth} />
37
41
  })
38
42
  return [<GroupRow label={groupName} colSpan={colSpan} key={`${tableName}-${groupName}`} />, ...rows]
39
43
  })
40
44
  : childrenMatrix.map((childRow, i) => {
45
+ let childRowCopy = [...childRow]
46
+ let rowType = undefined
47
+ if (hasRowType) rowType = childRowCopy.shift()
41
48
  const key = `${tableName}-row-${i}`
42
- return <Row key={key} rowKey={key} childRow={childRow} />
49
+ if (rowType === undefined) {
50
+ return <Row key={key} rowKey={key} childRow={childRow} wrapColumns={wrapColumns} cellMinWidth={tableOptions.cellMinWidth} />
51
+ } else {
52
+ switch (rowType) {
53
+ case RowType.row_group:
54
+ return <GroupRow label={childRowCopy[0]} colSpan={childRowCopy.length} key={key} />
55
+ case RowType.total:
56
+ return <Row key={key} rowKey={key} childRow={childRowCopy} isTotal={true} wrapColumns={wrapColumns} cellMinWidth={tableOptions.cellMinWidth} />
57
+ case RowType.row_group_total:
58
+ return <GroupRow label={childRowCopy[0]} colSpan={1} key={key} data={childRowCopy.slice(1)} />
59
+ default:
60
+ return <Row key={key} rowKey={key} childRow={childRowCopy} wrapColumns={wrapColumns} cellMinWidth={tableOptions.cellMinWidth} />
61
+ }
62
+ }
43
63
  })}
44
64
  </tbody>
45
65
  </table>
@@ -1,7 +1,7 @@
1
- const Cell = ({ children }) => {
1
+ const Cell = ({ children, style, isBold = false }) => {
2
2
  return (
3
- <td tabIndex={0} role='gridcell'>
4
- {children}
3
+ <td tabIndex={0} role='gridcell' style={style}>
4
+ {isBold ? <strong>{children}</strong> : children}
5
5
  </td>
6
6
  )
7
7
  }
@@ -1,14 +1,18 @@
1
+ import { ReactNode } from 'react'
2
+
1
3
  type GroupRowProps = {
2
- label: string
4
+ label: ReactNode
3
5
  colSpan: number
6
+ data?: ReactNode[]
4
7
  }
5
8
 
6
- const GroupRow = ({ label, colSpan }: GroupRowProps) => {
9
+ const GroupRow = ({ label, colSpan, data }: GroupRowProps) => {
7
10
  return (
8
11
  <tr>
9
12
  <th scope='colgroup' colSpan={colSpan}>
10
13
  {label}
11
14
  </th>
15
+ {data && data.map((item, i) => <th key={`${label}-${i}`}>{item}</th>)}
12
16
  </tr>
13
17
  )
14
18
  }
@@ -4,13 +4,20 @@ import Cell from './Cell'
4
4
  type RowProps = {
5
5
  childRow: ReactNode[]
6
6
  rowKey: string
7
+ wrapColumns: boolean
8
+ isTotal?: boolean
9
+ cellMinWidth?: number
7
10
  }
8
11
 
9
- const Row = ({ childRow, rowKey }: RowProps) => {
12
+ const Row = ({ childRow, rowKey, wrapColumns, cellMinWidth = 0, isTotal }: RowProps) => {
13
+ const whiteSpace = wrapColumns ? 'unset' : 'nowrap'
14
+ const minWidth = cellMinWidth + 'px'
10
15
  return (
11
16
  <tr>
12
17
  {childRow.map((child, i) => (
13
- <Cell key={rowKey + '__' + i}>{child}</Cell>
18
+ <Cell key={rowKey + '__' + i} style={{ whiteSpace, minWidth }} isBold={isTotal}>
19
+ {child}
20
+ </Cell>
14
21
  ))}
15
22
  </tr>
16
23
  )
@@ -0,0 +1,5 @@
1
+ export enum RowType {
2
+ row_group = 'row_group',
3
+ total = 'total',
4
+ row_group_total = 'row_group_total'
5
+ }
@@ -5,6 +5,8 @@ import './styles.scss'
5
5
  import Example_1 from './_mocks/dashboard_no_filter.json'
6
6
  import CityStateExample from './_mocks/example-city-state.json'
7
7
  import { displayGeoName } from '@cdc/map/src/helpers/displayGeoName'
8
+ import rowTypeData from './_mocks/row_type.json'
9
+ import { TableConfig } from '../DataTable/types/TableConfig'
8
10
 
9
11
  const meta: Meta<typeof DataTable> = {
10
12
  title: 'Components/Organisms/DataTable',
@@ -60,3 +62,42 @@ export const Grouped: Story = {
60
62
  tabbingId: datasetKey
61
63
  }
62
64
  }
65
+
66
+ export const RowType: Story = {
67
+ args: {
68
+ config: {
69
+ dashboard: {
70
+ theme: 'theme-blue',
71
+ title: 'RowType'
72
+ },
73
+ title: 'RowType',
74
+ dataUrl: '/examples/feature/__data__/ardi.json',
75
+ animate: false,
76
+ animateReplay: true,
77
+ palette: 'qualitative-soft',
78
+ aspectRatio: 1,
79
+ dataFormat: {
80
+ roundTo: 1,
81
+ commas: false,
82
+ prefix: '',
83
+ suffix: ''
84
+ },
85
+ legend: {
86
+ hide: false
87
+ },
88
+ table: {
89
+ label: 'Data Table',
90
+ expanded: true,
91
+ show: true,
92
+ customTableConfig: true
93
+ }
94
+ } as unknown as TableConfig,
95
+ dataConfig: { data: rowTypeData },
96
+ rawData: rowTypeData,
97
+ runtimeData: rowTypeData,
98
+ expandDataTable: true,
99
+ tableTitle: 'DataTable',
100
+ viewport: 'lg',
101
+ tabbingId: '#asdf'
102
+ }
103
+ }
@@ -0,0 +1,53 @@
1
+ import { Meta, StoryObj } from '@storybook/react'
2
+
3
+ import DataTableEditor from '../EditorPanel/DataTableEditor'
4
+ import { Accordion, AccordionItem, AccordionItemButton, AccordionItemHeading, AccordionItemPanel } from 'react-accessible-accordion'
5
+ import { useState } from 'react'
6
+
7
+ const EditorPanel = () => {
8
+ const { config, isDashboard } = Primary.args
9
+ const [_config, setConfig] = useState(config)
10
+ const updateField = (section, subsection, fieldName, value) => {
11
+ setConfig({
12
+ ..._config,
13
+ [section]: {
14
+ ..._config[section],
15
+ [fieldName]: value
16
+ }
17
+ })
18
+ }
19
+ return (
20
+ <Accordion>
21
+ <AccordionItem>
22
+ <AccordionItemHeading>
23
+ <AccordionItemButton>Data Table</AccordionItemButton>
24
+ </AccordionItemHeading>
25
+ <AccordionItemPanel>
26
+ <DataTableEditor config={_config} isDashboard={isDashboard} updateField={updateField} isLoadedFromUrl={false} />
27
+ </AccordionItemPanel>
28
+ </AccordionItem>
29
+ </Accordion>
30
+ )
31
+ }
32
+
33
+ const meta: Meta<typeof DataTableEditor> = {
34
+ title: 'Components/Organisms/EditorPanel',
35
+ component: EditorPanel
36
+ }
37
+
38
+ export default meta
39
+
40
+ type Story = StoryObj<typeof DataTableEditor>
41
+
42
+ export const Primary: Story = {
43
+ args: {
44
+ config: {
45
+ table: {
46
+ label: 'Data Table',
47
+ show: true
48
+ },
49
+ visualizationType: 'Pie'
50
+ },
51
+ isDashboard: true
52
+ }
53
+ }
@@ -0,0 +1,37 @@
1
+ import { Meta, StoryObj } from '@storybook/react'
2
+
3
+ import InputSelect from '../inputs/InputSelect'
4
+ import { useState } from 'react'
5
+
6
+ const Inputs: React.FC = ({ config }: any) => {
7
+ const [_config, setConfig] = useState(config)
8
+ const updateField = (section, subsection, fieldName, value) => {
9
+ setConfig({
10
+ ..._config,
11
+ [section]: {
12
+ ..._config[section],
13
+ [fieldName]: value
14
+ }
15
+ })
16
+ }
17
+ return (
18
+ <div>
19
+ <InputSelect label='Select' options={['apple', 'banana', 'orange']} fieldName='inputselect' updateField={updateField} />
20
+ </div>
21
+ )
22
+ }
23
+
24
+ const meta: Meta<typeof Inputs> = {
25
+ title: 'Components/Atoms/Inputs',
26
+ component: Inputs
27
+ }
28
+
29
+ export default meta
30
+
31
+ type Story = StoryObj<typeof Inputs>
32
+
33
+ export const Select: Story = {
34
+ args: {
35
+ config: {}
36
+ }
37
+ }
@@ -0,0 +1,24 @@
1
+ import { Meta, StoryObj } from '@storybook/react'
2
+
3
+ import MultiSelect from '../MultiSelect'
4
+
5
+ const meta: Meta<typeof MultiSelect> = {
6
+ title: 'Components/Molecules/MultiSelect',
7
+ component: MultiSelect
8
+ }
9
+
10
+ type Story = StoryObj<typeof MultiSelect>
11
+
12
+ export const Primary: Story = {
13
+ args: {
14
+ options: [
15
+ { value: '1', label: 'One' },
16
+ { value: '2', label: 'Two' },
17
+ { value: '3', label: 'Three' }
18
+ ],
19
+ label: 'MultiSelect',
20
+ updateField: (section, subsection, fieldName, value) => {}
21
+ }
22
+ }
23
+
24
+ export default meta