@cdc/core 4.24.3 → 4.24.4

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 (49) hide show
  1. package/assets/icon-command.svg +3 -0
  2. package/assets/icon-rotate-left.svg +3 -0
  3. package/components/AdvancedEditor.jsx +9 -0
  4. package/components/DataTable/DataTable.tsx +9 -3
  5. package/components/DataTable/components/ExpandCollapse.tsx +22 -16
  6. package/components/DataTable/helpers/chartCellMatrix.tsx +2 -2
  7. package/components/DataTable/helpers/mapCellMatrix.tsx +2 -2
  8. package/components/DataTable/types/TableConfig.ts +1 -0
  9. package/components/EditorPanel/ColumnsEditor.tsx +2 -1
  10. package/components/EditorPanel/DataTableEditor.tsx +17 -1
  11. package/components/Filters.jsx +8 -7
  12. package/components/Layout/components/Responsive.tsx +184 -0
  13. package/components/Layout/components/Sidebar/components/Sidebar.tsx +47 -0
  14. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +902 -0
  15. package/components/Layout/components/Sidebar/index.tsx +3 -0
  16. package/components/Layout/components/Visualization/index.tsx +79 -0
  17. package/components/Layout/components/Visualization/visualizations.scss +33 -0
  18. package/components/Layout/index.tsx +11 -0
  19. package/components/Layout/styles/editor-grid-view.scss +156 -0
  20. package/components/Layout/styles/editor-utils.scss +197 -0
  21. package/components/Layout/styles/editor.scss +144 -0
  22. package/components/LegendCircle.jsx +4 -3
  23. package/components/MediaControls.jsx +1 -1
  24. package/components/Table/Table.tsx +7 -5
  25. package/components/Table/components/Row.tsx +6 -2
  26. package/components/Table/types/RowType.ts +3 -0
  27. package/components/Waiting.jsx +11 -1
  28. package/components/_stories/styles.scss +1 -0
  29. package/components/createBarElement.jsx +37 -34
  30. package/components/elements/SkipTo.tsx +37 -5
  31. package/components/managers/DataDesigner.tsx +18 -18
  32. package/components/ui/Icon.tsx +5 -1
  33. package/helpers/{coveUpdateWorker.js → coveUpdateWorker.ts} +7 -7
  34. package/helpers/useDataVizClasses.js +5 -5
  35. package/helpers/ver/4.24.3.ts +56 -0
  36. package/package.json +2 -2
  37. package/styles/_data-table.scss +8 -0
  38. package/styles/_global.scss +7 -4
  39. package/styles/_variables.scss +3 -0
  40. package/styles/base.scss +0 -18
  41. package/styles/v2/base/index.scss +1 -1
  42. package/styles/v2/components/ui/tooltip.scss +0 -21
  43. package/types/Axis.ts +2 -0
  44. package/types/ConfigureData.ts +8 -0
  45. package/types/DataDescription.ts +9 -0
  46. package/types/Table.ts +1 -0
  47. package/types/Visualization.ts +3 -6
  48. package/helpers/ver/4.23.js +0 -10
  49. package/helpers/ver/4.24.3.js +0 -25
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" focusable="false" role="img" fill="currentColor">
2
+ <path d="M404.83356,297.97872H354.67715V214.177h50.15641a107.08854,107.08854,0,1,0,0-214.177C345.64284,0,297.51145,47.97571,297.82294,107.01074v50.46778H214.33287V107.01074A107.09452,107.09452,0,0,0,107.32225,0C47.97572,0,0,47.97571,0,107.01074c0,59.3464,48.28721,107.6336,107.32225,107.16629h50.15628v83.80169H107.32225C47.97572,297.97872,0,345.79862,0,404.83352a107.16644,107.16644,0,1,0,214.33287,0V354.83291h83.49007v50.00061a107.08879,107.08879,0,1,0,107.01062-106.8548ZM354.67715,106.85493a50.2348,50.2348,0,1,1,50.0006,50.46791h-50.0006ZM157.47853,404.83352a50.2348,50.2348,0,1,1-50.0006-50.00061h50.0006Zm0-247.51068h-50.0006a50.2348,50.2348,0,1,1,50.0006-50.46791ZM297.97862,297.97872H214.33287V214.33285h83.64575Zm106.69913,157.3227a50.15888,50.15888,0,0,1-50.0006-50.4679V354.83291h50.0006a50.2348,50.2348,0,1,1,0,100.46851Z"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" focusable="false" role="img" fill="currentColor">
2
+ <path d="M480 256c0 123.4-100.5 223.9-223.9 223.9c-48.84 0-95.17-15.58-134.2-44.86c-14.12-10.59-16.97-30.66-6.375-44.81c10.59-14.12 30.62-16.94 44.81-6.375c27.84 20.91 61 31.94 95.88 31.94C344.3 415.8 416 344.1 416 256s-71.69-159.8-159.8-159.8c-37.46 0-73.09 13.49-101.3 36.64l45.12 45.14c17.01 17.02 4.955 46.1-19.1 46.1H35.17C24.58 224.1 16 215.5 16 204.9V59.04c0-24.04 29.07-36.08 46.07-19.07l47.6 47.63C149.9 52.71 201.5 32.11 256.1 32.11C379.5 32.11 480 132.6 480 256z"/>
3
+ </svg>
@@ -51,6 +51,15 @@ export const AdvancedEditor = ({ loadConfig, state, convertStateToConfig }) => {
51
51
  <p className='pb-2'>
52
52
  This tool displays the actual <acronym title='JavaScript Object Notation'>JSON</acronym> configuration that is generated by this editor and allows you to edit properties directly and apply them.
53
53
  </p>
54
+ <button
55
+ className='btn'
56
+ onClick={() => {
57
+ navigator.clipboard.writeText(configTextboxValue)
58
+ alert('Copied!')
59
+ }}
60
+ >
61
+ Copy to Clipboard
62
+ </button>
54
63
  <textarea value={configTextboxValue} onChange={event => setConfigTextbox(event.target.value)} />
55
64
  <button className='btn full-width' onClick={() => loadConfig(JSON.parse(configTextboxValue))}>
56
65
  Apply
@@ -44,7 +44,7 @@ export type DataTableProps = {
44
44
  showDownloadButton?: boolean
45
45
  tabbingId: string
46
46
  tableTitle: string
47
- viewport: string
47
+ viewport: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
48
48
  vizTitle?: string
49
49
  // determines if columns should be wrapped in the table
50
50
  wrapColumns?: boolean
@@ -185,22 +185,25 @@ const DataTable = (props: DataTableProps) => {
185
185
  </MediaControls.Section>
186
186
  <section id={tabbingId.replace('#', '')} className={`data-table-container ${viewport}`} aria-label={accessibilityLabel}>
187
187
  <SkipTo skipId={skipId} skipMessage='Skip Data Table' />
188
- <ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
188
+ <ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} fontSize={config.fontSize} viewport={viewport} />
189
189
  <div className='table-container' style={limitHeight}>
190
190
  <Table
191
+ viewport={viewport}
191
192
  wrapColumns={wrapColumns}
192
- childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData: _runtimeData }) : chartCellMatrix({ rows, ...props, runtimeData: _runtimeData, isVertical, sortBy, hasRowType })}
193
+ childrenMatrix={config.type === 'map' ? mapCellMatrix({ rows, wrapColumns, ...props, runtimeData: _runtimeData, viewport }) : chartCellMatrix({ rows, ...props, runtimeData: _runtimeData, isVertical, sortBy, hasRowType, viewport })}
193
194
  tableName={config.type}
194
195
  caption={caption}
195
196
  stickyHeader
196
197
  hasRowType={hasRowType}
197
198
  headContent={config.type === 'map' ? <MapHeader columns={columns} {...props} sortBy={sortBy} setSortBy={setSortBy} /> : <ChartHeader data={_runtimeData} {...props} hasRowType={hasRowType} isVertical={isVertical} sortBy={sortBy} setSortBy={setSortBy} />}
198
199
  tableOptions={{ className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}${isVertical ? '' : ' horizontal'}`, 'aria-live': 'assertive', 'aria-rowcount': config?.data?.length ? config.data.length : -1, hidden: !expanded }}
200
+ fontSize={config.fontSize}
199
201
  />
200
202
 
201
203
  {/* REGION Data Table */}
202
204
  {noRelativeRegions && config.regions && config.regions.length > 0 && config.visualizationType !== 'Box Plot' && (
203
205
  <Table
206
+ viewport={viewport}
204
207
  wrapColumns={wrapColumns}
205
208
  childrenMatrix={regionCellMatrix({ config })}
206
209
  tableName={config.visualizationType}
@@ -213,6 +216,7 @@ const DataTable = (props: DataTableProps) => {
213
216
  </tr>
214
217
  }
215
218
  tableOptions={{ className: 'region-table data-table' }}
219
+ fontSize={config.fontSize}
216
220
  />
217
221
  )}
218
222
  </div>
@@ -231,6 +235,7 @@ const DataTable = (props: DataTableProps) => {
231
235
  <ExpandCollapse expanded={expanded} setExpanded={setExpanded} tableTitle={tableTitle} />
232
236
  <div className='table-container' style={limitHeight}>
233
237
  <Table
238
+ viewport={viewport}
234
239
  wrapColumns={wrapColumns}
235
240
  childrenMatrix={boxplotCellMatrix({ rows: tableData, config })}
236
241
  tableName={config.visualizationType}
@@ -238,6 +243,7 @@ const DataTable = (props: DataTableProps) => {
238
243
  stickyHeader
239
244
  headContent={<BoxplotHeader categories={config.boxplot.categories} />}
240
245
  tableOptions={{ className: `${expanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}`, 'aria-live': 'assertive', 'aria-rowcount': 11, hidden: !expanded }}
246
+ fontSize={config.fontSize}
241
247
  />
242
248
  </div>
243
249
  </section>
@@ -1,21 +1,27 @@
1
1
  import Icon from '../../ui/Icon'
2
2
 
3
- const ExpandCollapse = ({ expanded, setExpanded, tableTitle }) => (
4
- <div
5
- className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
6
- onClick={() => {
7
- setExpanded(!expanded)
8
- }}
9
- tabIndex={0}
10
- onKeyDown={e => {
11
- if (e.keyCode === 13) {
3
+ const ExpandCollapse = ({ expanded, setExpanded, tableTitle, fontSize, viewport }) => {
4
+ const fontSizes = { small: 16, medium: 18, large: 20 }
5
+ const titleFontSize = ['sm', 'xs', 'xxs'].includes(viewport) ? '13px' : `${fontSizes[fontSize]}px`
6
+ return (
7
+ <div
8
+ style={{ fontSize: titleFontSize }}
9
+ role='button'
10
+ className={expanded ? 'data-table-heading' : 'collapsed data-table-heading'}
11
+ onClick={() => {
12
12
  setExpanded(!expanded)
13
- }
14
- }}
15
- >
16
- <Icon display={expanded ? 'minus' : 'plus'} base />
17
- {tableTitle}
18
- </div>
19
- )
13
+ }}
14
+ tabIndex={0}
15
+ onKeyDown={e => {
16
+ if (e.keyCode === 13) {
17
+ setExpanded(!expanded)
18
+ }
19
+ }}
20
+ >
21
+ <Icon display={expanded ? 'minus' : 'plus'} base />
22
+ {tableTitle}
23
+ </div>
24
+ )
25
+ }
20
26
 
21
27
  export default ExpandCollapse
@@ -15,7 +15,7 @@ type ChartRowsProps = DataTableProps & {
15
15
  hasRowType?: boolean
16
16
  }
17
17
 
18
- const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, groupBy, hasRowType }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
18
+ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorScale, groupBy, hasRowType, viewport }: ChartRowsProps): CellMatrix | GroupCellMatrix => {
19
19
  const dataSeriesColumns = getDataSeriesColumns(config, isVertical, runtimeData)
20
20
 
21
21
  const dataSeriesColumnsSorted = () => {
@@ -79,7 +79,7 @@ const chartCellArray = ({ rows, runtimeData, config, isVertical, sortBy, colorSc
79
79
  config.visualizationType !== 'Pie'
80
80
  ? [
81
81
  <>
82
- {colorScale && colorScale(seriesName) && <LegendCircle fill={colorScale(seriesName)} />}
82
+ {colorScale && colorScale(seriesName) && <LegendCircle viewport={viewport} fill={colorScale(seriesName)} />}
83
83
  {seriesName}
84
84
  </>
85
85
  ]
@@ -7,7 +7,7 @@ type MapRowsProps = DataTableProps & {
7
7
  rows: string[]
8
8
  }
9
9
 
10
- const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, displayGeoName, formatLegendLocation, displayDataAsText, navigationHandler, setFilteredCountryCode }: MapRowsProps): ReactNode[][] => {
10
+ const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, displayGeoName, formatLegendLocation, displayDataAsText, navigationHandler, setFilteredCountryCode, viewport }: MapRowsProps): ReactNode[][] => {
11
11
  return rows.map(row =>
12
12
  Object.keys(columns)
13
13
  .filter(column => columns[column].dataTable === true && columns[column].name)
@@ -31,7 +31,7 @@ const mapCellArray = ({ rows, columns, runtimeData, config, applyLegendToRow, di
31
31
  }
32
32
  cellValue = (
33
33
  <div className='col-12'>
34
- <LegendCircle fill={legendColor[0]} />
34
+ <LegendCircle viewport={viewport} fill={legendColor[0]} />
35
35
  <CellAnchor markup={labelValue} row={rowObj} columns={columns} navigationHandler={navigationHandler} mapZoomHandler={mapZoomHandler} />
36
36
  </div>
37
37
  )
@@ -24,4 +24,5 @@ export type TableConfig = {
24
24
  dataFormat?: Object
25
25
  runtime?: Runtime
26
26
  data: Object[]
27
+ fontSize: 'small' | 'medium' | 'large'
27
28
  }
@@ -31,9 +31,10 @@ const ColumnsEditor: React.FC<ColumnsEditorProps> = ({ config, updateField, dele
31
31
  // just adds a new column but not set to any data yet
32
32
  const addAdditionalColumn = number => {
33
33
  const columnKey = `additionalColumn${number}`
34
+ const showInViz = config.type === 'table'
34
35
  const newColumn: Column = {
35
36
  label: 'New Column',
36
- dataTable: false,
37
+ dataTable: showInViz,
37
38
  tooltips: false,
38
39
  prefix: '',
39
40
  suffix: '',
@@ -75,7 +75,23 @@ const DataTable: React.FC<DataTableProps> = ({ config, updateField, isDashboard,
75
75
  }
76
76
  />
77
77
  )}
78
- <TextField value={config.table.indexLabel} section='table' fieldName='indexLabel' label='Index Column Header' updateField={updateField} />
78
+ <TextField
79
+ value={config.table.indexLabel}
80
+ section='table'
81
+ fieldName='indexLabel'
82
+ label='Index Column Header'
83
+ updateField={updateField}
84
+ tooltip={
85
+ <Tooltip style={{ textTransform: 'none' }}>
86
+ <Tooltip.Target>
87
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
88
+ </Tooltip.Target>
89
+ <Tooltip.Content>
90
+ <p>To comply with 508 standards, if the first column in the data table has no header, enter a brief one here.</p>
91
+ </Tooltip.Content>
92
+ </Tooltip>
93
+ }
94
+ />
79
95
  <TextField
80
96
  value={config.table.caption}
81
97
  updateField={updateField}
@@ -239,16 +239,17 @@ const Filters = props => {
239
239
  const Filters = props => props.children
240
240
 
241
241
  const filterSectionClassList = ['filters-section', type === 'map' ? general.headerColor : visualizationConfig?.visualizationType === 'Spark Line' ? null : theme]
242
-
243
242
  // Exterior Section Wrapper
244
243
  Filters.Section = props => {
245
244
  return (
246
- <section className={filterSectionClassList.join(' ')}>
247
- <p className='filters-section__intro-text'>
248
- {filterConstants.introText} {visualizationConfig.filterBehavior === 'Apply Button' && filterConstants.applyText}
249
- </p>
250
- <div className='filters-section__wrapper'>{props.children}</div>
251
- </section>
245
+ visualizationConfig?.filters && (
246
+ <section className={filterSectionClassList.join(' ')}>
247
+ <p className='filters-section__intro-text'>
248
+ {filters?.some(f => f.active) ? filterConstants.introText : ''} {visualizationConfig.filterBehavior === 'Apply Button' && filterConstants.applyText}
249
+ </p>
250
+ <div className='filters-section__wrapper'>{props.children}</div>
251
+ </section>
252
+ )
252
253
  )
253
254
  }
254
255
 
@@ -0,0 +1,184 @@
1
+ import React, { useState, useRef, useEffect, useCallback } from 'react'
2
+ import '../styles/editor-utils.scss'
3
+ import '../styles/editor.scss'
4
+
5
+ import Icon from '../../ui/Icon'
6
+
7
+ const breakpoints = [
8
+ '360', // xxs (mobile) 0 - 360
9
+ '480', // xs
10
+ '768', // sm
11
+ '960', // md
12
+ '1170', // lg
13
+ '1280' // xl
14
+ ]
15
+
16
+ const os = navigator.userAgent.indexOf('Win') !== -1 ? 'Win' : navigator.userAgent.indexOf('Mac') !== -1 ? 'MacOS' : null
17
+
18
+ const Responsive = ({ children, isEditor }) => {
19
+ const [displayPanel, setDisplayPanel] = useState(false)
20
+ const [displayGrid, setDisplayGrid] = useState(false)
21
+ const [viewportPreview, setViewportPreview] = useState(null)
22
+ const [rotateAnimation, setRotateAnimation] = useState(false)
23
+ const [showConfirm, setShowConfirm] = useState(false)
24
+ const [previewDimensions, setPreviewDimensions] = useState<{ width: number; height: number }>(null)
25
+
26
+ const resetIcon = useRef(null)
27
+ const editorPanelRef = useRef(null)
28
+ const componentContainerRef = useRef(null)
29
+
30
+ const viewportPreviewController = useCallback(
31
+ breakpoint => {
32
+ return setViewportPreview(prevState => (prevState !== breakpoint ? breakpoint : null))
33
+ },
34
+ [viewportPreview]
35
+ )
36
+
37
+ const onKeypress = key => {
38
+ if (key.code === 'KeyL' && key.ctrlKey) setDisplayPanel(display => !display)
39
+ const viewportCommandKey = os === 'MacOS' ? key.metaKey : key.altKey
40
+ if (viewportCommandKey) {
41
+ let keyIndex = key.key
42
+
43
+ // Validates that the hotkey pressed is a number, and that
44
+ // the number is within the range of the provided breakpoint list range.
45
+ if (!isNaN(keyIndex)) {
46
+ if (keyIndex <= breakpoints.length) {
47
+ key.preventDefault()
48
+ viewportPreviewController(breakpoints[keyIndex - 1])
49
+ }
50
+ }
51
+ }
52
+
53
+ if (!viewportCommandKey) {
54
+ if (key.code === 'KeyG') setDisplayGrid(display => !display)
55
+ if (key.code === 'KeyR') resetPreview()
56
+ }
57
+ }
58
+
59
+ // Set and clean up the event listener for the hotkeys
60
+ useEffect(() => {
61
+ document.addEventListener('keydown', onKeypress)
62
+ return () => document.removeEventListener('keydown', onKeypress)
63
+ }, [])
64
+
65
+ //Reset Viewport Preview
66
+ const resetPreview = useCallback(() => {
67
+ if (!rotateAnimation && resetIcon.current) {
68
+ setViewportPreview(null)
69
+ setRotateAnimation(true)
70
+ setDisplayGrid(false)
71
+ resetIcon.current.style.transition = 'transform 800ms cubic-bezier(0.16, 1, 0.3, 1)'
72
+ resetIcon.current.style.transform = 'rotate(-360deg)'
73
+
74
+ const timeoutShow = setTimeout(() => {
75
+ setRotateAnimation(false)
76
+ resetIcon.current.style.transition = null
77
+ resetIcon.current.style.transform = 'rotate(0deg)'
78
+ resetIcon.current.style.transform = null
79
+ }, 400)
80
+
81
+ return () => clearTimeout(timeoutShow)
82
+ }
83
+ }, [rotateAnimation])
84
+
85
+ // Toggle the grid display with the viewport preview
86
+ useEffect(() => {
87
+ return viewportPreview ? setDisplayGrid(true) : setDisplayGrid(false)
88
+ }, [viewportPreview])
89
+
90
+ // Observe and set editor component widths
91
+ useEffect(() => {
92
+ if (!componentContainerRef.current) return
93
+
94
+ let resizeObserver = new ResizeObserver(entries => {
95
+ for (let entry of entries) {
96
+ let { width, height } = entry.contentRect
97
+ setPreviewDimensions({ width, height })
98
+ }
99
+ })
100
+
101
+ resizeObserver.observe(componentContainerRef.current)
102
+
103
+ return () => {
104
+ if (!resizeObserver) return
105
+ resizeObserver.disconnect()
106
+ resizeObserver = null
107
+ }
108
+ })
109
+
110
+ const onBackClick = () => setDisplayPanel(!displayPanel)
111
+
112
+ if (!isEditor || !displayPanel) return children
113
+
114
+ return (
115
+ <div className='cove-editor__content' data-grid={displayGrid || null}>
116
+ <div className='cove-editor__content-wrap--x' style={viewportPreview ? { maxWidth: viewportPreview + 'px', minWidth: 'unset' } : null}>
117
+ <div className='cove-editor__content-wrap--y'>
118
+ <div className='cove-editor-utils__breakpoints--px'>
119
+ {displayGrid && displayPanel && (
120
+ <>
121
+ {Math.round(previewDimensions.width)}
122
+ <span className='mx-1' style={{ fontSize: '0.675rem' }}>
123
+
124
+ </span>
125
+ {Math.round(previewDimensions.height)}
126
+ </>
127
+ )}
128
+ </div>
129
+ <div className='cove-editor__grid-caret--top' ref={componentContainerRef}>
130
+ <div className='cove-editor__grid-caret--bottom'>{children}</div>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ <div className='cove-editor-utils__hotkeys'>
135
+ <div className='cove-editor-utils__hotkeys--left'>
136
+ <p className={displayPanel ? 'hotkey--active' : null}>Editor</p>
137
+ <p className={displayGrid ? 'hotkey--active' : null}>Grid</p>
138
+ <p className={rotateAnimation ? 'hotkey--active' : null}>Reset</p>
139
+ <p className={viewportPreview ? 'hotkey--active' : null}>View</p>
140
+ </div>
141
+ <div className='cove-editor-utils__hotkeys--right'>
142
+ <p className={displayPanel ? 'hotkey--active' : null}>esc</p>
143
+ <p className={displayGrid ? 'hotkey--active' : null}>G</p>
144
+ <p className={rotateAnimation ? 'hotkey--active' : null}>R</p>
145
+ <p className={viewportPreview ? 'hotkey--active' : null}>
146
+ {os === 'MacOS' ? <Icon style={{ marginRight: '0.25rem' }} display='command' size={12} /> : 'Alt'} + {viewportPreview ? breakpoints.indexOf(viewportPreview) + 1 : `[1 - ${breakpoints.length}]`}
147
+ </p>
148
+ </div>
149
+ </div>
150
+
151
+ <div className='cove-editor-utils__breakpoints'>
152
+ <ul className={`cove-editor-utils__breakpoints-list${viewportPreview ? ' has-active' : ''}`}>
153
+ <button
154
+ className='cove-editor-utils__breakpoints-item'
155
+ onClick={() => {
156
+ setDisplayGrid(display => !display)
157
+ }}
158
+ >
159
+ <div className='cove-editor-utils__breakpoints-grid'>
160
+ <Icon display='grid' />
161
+ </div>
162
+ </button>
163
+ {breakpoints.map((breakpoint, index) => (
164
+ <button className={`cove-editor-utils__breakpoints-item${viewportPreview === breakpoint ? ' active' : ''}`} onClick={() => viewportPreviewController(breakpoint)} key={index}>
165
+ {breakpoint}px
166
+ </button>
167
+ ))}
168
+ <button
169
+ className='cove-editor-utils__breakpoints-item'
170
+ onClick={() => {
171
+ resetPreview()
172
+ }}
173
+ >
174
+ <div className='cove-editor-utils__breakpoints-reset' ref={resetIcon}>
175
+ <Icon display='rotateLeft' />
176
+ </div>
177
+ </button>
178
+ </ul>
179
+ </div>
180
+ </div>
181
+ )
182
+ }
183
+
184
+ export default Responsive
@@ -0,0 +1,47 @@
1
+ import React from 'react'
2
+ import './sidebar.styles.scss'
3
+
4
+ type SidebarProps = {
5
+ // whether or not the viz is within a dashboard
6
+ isDashboard: boolean
7
+ // show/hide the sidebar
8
+ displayPanel: boolean
9
+ // sidebarTitle
10
+ title: string
11
+ // inner content
12
+ children: React.ReactNode
13
+ // on arrow toggle
14
+ onBackClick: () => void
15
+ }
16
+
17
+ const Sidebar: React.FC<SidebarProps> = props => {
18
+ const { displayPanel = false, isDashboard = false, title = 'Configure Visualization', children, onBackClick } = props
19
+
20
+ const getSectionClasses = () => {
21
+ const sectionClasses = ['editor-panel', 'cove', 'sidebar']
22
+ if (!displayPanel) sectionClasses.push('hidden')
23
+ if (isDashboard) sectionClasses.push('dashboard')
24
+ return sectionClasses
25
+ }
26
+
27
+ const getButtonClasses = () => {
28
+ const buttonClasses = []
29
+ if (displayPanel) buttonClasses.push('editor-panel__toggle')
30
+ if (!displayPanel) buttonClasses.push('collapsed', 'editor-panel__toggle')
31
+ return buttonClasses
32
+ }
33
+
34
+ return (
35
+ <>
36
+ <button className={getButtonClasses().join(' ')} title={displayPanel ? `Collapse Editor` : `Expand Editor`} onClick={onBackClick}></button>
37
+ <section className={getSectionClasses().join(' ')}>
38
+ <h2 className='editor-panel__title'>{title}</h2>
39
+ <section className='form-container' data-html2canvas-ignore>
40
+ {children}
41
+ </section>
42
+ </section>
43
+ </>
44
+ )
45
+ }
46
+
47
+ export default Sidebar