@cdc/markup-include 4.26.2 → 4.26.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.
- package/CONFIG.md +113 -0
- package/README.md +38 -14
- package/dist/cdcmarkupinclude.js +20835 -12893
- package/examples/minimal-example.json +11 -0
- package/package.json +4 -4
- package/src/CdcMarkupInclude.tsx +192 -108
- package/src/ConfigContext.ts +1 -0
- package/src/_stories/MarkupInclude.Editor.stories.tsx +57 -17
- package/src/_stories/MarkupInclude.smoke.stories.tsx +33 -0
- package/src/_stories/MarkupInclude.stories.tsx +81 -0
- package/src/cdcMarkupInclude.style.css +15 -11
- package/src/components/EditorPanel/EditorPanel.styles.css +1 -1
- package/src/components/EditorPanel/EditorPanel.tsx +271 -16
- package/src/data/initial-state.js +7 -1
- package/src/scss/main.scss +81 -10
- package/src/test/CdcMarkupInclude.test.jsx +74 -3
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { expect } from 'storybook/test'
|
|
2
3
|
import CdcMarkupInclude from '../CdcMarkupInclude'
|
|
4
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
3
5
|
import primary from './_mock/primary.json'
|
|
4
6
|
import noConditions from './_mock/no-conditions.json'
|
|
5
7
|
import withConditions from './_mock/with-conditions.json'
|
|
@@ -48,6 +50,63 @@ export const icon_no_text: Story = {
|
|
|
48
50
|
isEditor: false
|
|
49
51
|
}
|
|
50
52
|
}
|
|
53
|
+
|
|
54
|
+
export const Icon_Sizing: Story = {
|
|
55
|
+
args: {
|
|
56
|
+
config: {
|
|
57
|
+
...primary,
|
|
58
|
+
contentEditor: {
|
|
59
|
+
...primary.contentEditor,
|
|
60
|
+
showHeader: true,
|
|
61
|
+
title: 'SVG icon sizing',
|
|
62
|
+
useInlineHTML: true,
|
|
63
|
+
markupVariables: [
|
|
64
|
+
{
|
|
65
|
+
sourceType: 'icon',
|
|
66
|
+
name: 'link-external',
|
|
67
|
+
tag: '{{link-external}}',
|
|
68
|
+
iconId: 'link-external',
|
|
69
|
+
conditions: []
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
inlineHTML: `
|
|
73
|
+
<p class="icon-sizing-inline">Rate <span class="icon-sizing-inline-target">{{link-external}}</span> increased</p>
|
|
74
|
+
<div
|
|
75
|
+
class="icon-sizing-parent-target"
|
|
76
|
+
data-cove-svg-size="parent-height"
|
|
77
|
+
style="height: 200px; background: #f8d7da; display: inline-flex; align-items: center;"
|
|
78
|
+
>
|
|
79
|
+
{{link-external}}
|
|
80
|
+
</div>
|
|
81
|
+
`
|
|
82
|
+
},
|
|
83
|
+
enableMarkupVariables: true
|
|
84
|
+
} as any,
|
|
85
|
+
isEditor: false
|
|
86
|
+
},
|
|
87
|
+
play: async ({ canvasElement }) => {
|
|
88
|
+
await assertVisualizationRendered(canvasElement)
|
|
89
|
+
|
|
90
|
+
const inlineIcon = canvasElement.querySelector('.icon-sizing-inline-target .cove-inline-svg') as HTMLElement | null
|
|
91
|
+
const parentIcon = canvasElement.querySelector('.icon-sizing-parent-target .cove-inline-svg') as HTMLElement | null
|
|
92
|
+
const parentContainer = canvasElement.querySelector('.icon-sizing-parent-target') as HTMLElement | null
|
|
93
|
+
|
|
94
|
+
expect(inlineIcon).toBeTruthy()
|
|
95
|
+
expect(parentIcon).toBeTruthy()
|
|
96
|
+
expect(parentContainer).toBeTruthy()
|
|
97
|
+
|
|
98
|
+
const inlineHeight = inlineIcon!.getBoundingClientRect().height
|
|
99
|
+
const parentHeight = parentIcon!.getBoundingClientRect().height
|
|
100
|
+
const containerHeight = parentContainer!.getBoundingClientRect().height
|
|
101
|
+
|
|
102
|
+
expect(inlineHeight).toBeGreaterThan(0)
|
|
103
|
+
expect(inlineHeight).toBeLessThan(50)
|
|
104
|
+
expect(parentHeight).toBeGreaterThan(inlineHeight * 3)
|
|
105
|
+
expect(parentHeight).toBeGreaterThan(180)
|
|
106
|
+
expect(Math.abs(parentHeight - containerHeight)).toBeLessThan(2)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
51
110
|
export const image_with_text: Story = {
|
|
52
111
|
args: {
|
|
53
112
|
config: imageWithText,
|
|
@@ -55,4 +114,26 @@ export const image_with_text: Story = {
|
|
|
55
114
|
}
|
|
56
115
|
}
|
|
57
116
|
|
|
117
|
+
export const TP5_Test: Story = {
|
|
118
|
+
args: {
|
|
119
|
+
config: {
|
|
120
|
+
...primary,
|
|
121
|
+
contentEditor: {
|
|
122
|
+
...primary.contentEditor,
|
|
123
|
+
style: 'tp5',
|
|
124
|
+
title: 'TP5 Markup Include Test',
|
|
125
|
+
useInlineHTML: true,
|
|
126
|
+
inlineHTML:
|
|
127
|
+
'<p>This is a TP5 style markup include test story.</p><p>Clear the title in the editor to verify TP5 title hide behavior.</p>'
|
|
128
|
+
},
|
|
129
|
+
visual: {
|
|
130
|
+
...primary.visual,
|
|
131
|
+
border: true,
|
|
132
|
+
whiteBackground: true
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
isEditor: true
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
58
139
|
export default meta
|
|
@@ -1,39 +1,43 @@
|
|
|
1
|
-
.
|
|
1
|
+
.cove-visualization.markup-include {
|
|
2
2
|
.spacing-wrapper {
|
|
3
3
|
background-color: var(--lightGray) !important;
|
|
4
4
|
}
|
|
5
|
+
|
|
5
6
|
.cove-tooltip-variable {
|
|
6
7
|
display: none;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
.markup-include-content-container.cove-
|
|
10
|
-
.markup-include-component.cove-
|
|
10
|
+
c .markup-include-content-container.cove-visualization__inner {
|
|
11
|
+
.markup-include-component.cove-visualization__body:not(.component--hide-background-color, .component--has-background) {
|
|
11
12
|
background-color: white;
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
.
|
|
17
|
+
.cove-visualization.markup-include.is-editor {
|
|
17
18
|
.cove-tooltip-variable {
|
|
18
|
-
position: relative;
|
|
19
|
-
display: inline;
|
|
20
19
|
background: var(--orange-tertiary);
|
|
20
|
+
display: inline;
|
|
21
|
+
position: relative;
|
|
22
|
+
|
|
21
23
|
.cove-tooltip-value {
|
|
22
|
-
display: none;
|
|
23
|
-
position: absolute;
|
|
24
24
|
background: var(--white);
|
|
25
25
|
border: 1px solid black;
|
|
26
|
+
display: none;
|
|
26
27
|
font-size: 16px;
|
|
28
|
+
max-width: 500px;
|
|
27
29
|
padding: 10px;
|
|
30
|
+
position: absolute;
|
|
28
31
|
width: 100vw;
|
|
29
|
-
max-width: 500px;
|
|
30
32
|
z-index: 9999;
|
|
31
33
|
}
|
|
32
|
-
|
|
34
|
+
|
|
35
|
+
&:hover>.cove-tooltip-value {
|
|
33
36
|
display: block;
|
|
34
37
|
}
|
|
35
38
|
}
|
|
39
|
+
|
|
36
40
|
.cove-markup-include-variable-value {
|
|
37
41
|
display: none !important;
|
|
38
42
|
}
|
|
39
|
-
}
|
|
43
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { useContext, useRef } from 'react'
|
|
1
|
+
import { useContext, useMemo, useRef } from 'react'
|
|
2
2
|
|
|
3
3
|
// Context
|
|
4
4
|
import ConfigContext from '../../ConfigContext'
|
|
5
5
|
|
|
6
6
|
// Helpers
|
|
7
7
|
import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
|
|
8
|
+
import { useDataColumns } from '@cdc/core/hooks/useDataColumns'
|
|
8
9
|
|
|
9
10
|
// Components
|
|
10
11
|
import { EditorPanel as BaseEditorPanel } from '@cdc/core/components/EditorPanel/EditorPanel'
|
|
@@ -14,7 +15,11 @@ import Icon from '@cdc/core/components/ui/Icon'
|
|
|
14
15
|
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
15
16
|
import MarkupVariablesEditor from '@cdc/core/components/EditorPanel/components/MarkupVariablesEditor'
|
|
16
17
|
import FootnotesEditor from '@cdc/core/components/EditorPanel/FootnotesEditor'
|
|
17
|
-
import
|
|
18
|
+
import StyleTreatmentSection from '@cdc/core/components/EditorPanel/sections/StyleTreatmentSection'
|
|
19
|
+
import { HeaderThemeSelector } from '@cdc/core/components/HeaderThemeSelector'
|
|
20
|
+
import { DataColorSelector } from '@cdc/core/components/DataColorSelector'
|
|
21
|
+
import { DATA_COLOR_PRESETS } from '@cdc/core/helpers/dataColors'
|
|
22
|
+
import Button from '@cdc/core/components/elements/Button'
|
|
18
23
|
import { Datasets } from '@cdc/core/types/DataSet'
|
|
19
24
|
|
|
20
25
|
// styles
|
|
@@ -25,10 +30,120 @@ type MarkupIncludeEditorPanelProps = {
|
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
const EditorPanel: React.FC<MarkupIncludeEditorPanelProps> = ({ datasets }) => {
|
|
28
|
-
const { config, data, isDashboard, loading, setParentConfig, updateConfig } = useContext(ConfigContext)
|
|
29
|
-
const { contentEditor, theme, visual } = config
|
|
33
|
+
const { config, data, editorData, isDashboard, loading, setParentConfig, updateConfig } = useContext(ConfigContext)
|
|
34
|
+
const { contentEditor, theme, visual } = config || {}
|
|
30
35
|
const { inlineHTML, srcUrl, title, useInlineHTML } = contentEditor || {}
|
|
36
|
+
const isTp5Style = contentEditor?.style === 'tp5'
|
|
31
37
|
const updateField = updateFieldFactory(config, updateConfig, true)
|
|
38
|
+
const styleTreatment = (visual as any)?.tp5Treatment ? 'tp5' : 'legacy'
|
|
39
|
+
const markupEditorData = Array.isArray(editorData) ? editorData : data || []
|
|
40
|
+
const columns = useDataColumns(markupEditorData)
|
|
41
|
+
const dataColorMappings = config.dataColors?.mappings || []
|
|
42
|
+
|
|
43
|
+
const dataColorValues = useMemo(() => {
|
|
44
|
+
const colorColumn = config.dataColors?.column
|
|
45
|
+
if (!colorColumn) return []
|
|
46
|
+
|
|
47
|
+
const uniqueValues = new Set<string>()
|
|
48
|
+
markupEditorData?.forEach(row => {
|
|
49
|
+
const value = row?.[colorColumn]
|
|
50
|
+
if (value !== undefined && value !== null) {
|
|
51
|
+
uniqueValues.add(String(value))
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
return Array.from(uniqueValues).sort()
|
|
56
|
+
}, [markupEditorData, config.dataColors?.column])
|
|
57
|
+
|
|
58
|
+
type DataColorDisplayEntry = { sourceValue: string; fromData: boolean; key: string }
|
|
59
|
+
|
|
60
|
+
const dataColorDisplayList = useMemo<DataColorDisplayEntry[]>(() => {
|
|
61
|
+
const dataSet = new Set(dataColorValues)
|
|
62
|
+
const list: DataColorDisplayEntry[] = dataColorValues.map(v => ({
|
|
63
|
+
sourceValue: v,
|
|
64
|
+
fromData: true,
|
|
65
|
+
key: `data-${v}`
|
|
66
|
+
}))
|
|
67
|
+
dataColorMappings.forEach((m, i) => {
|
|
68
|
+
if (!dataSet.has(m.sourceValue)) {
|
|
69
|
+
list.push({ sourceValue: m.sourceValue, fromData: false, key: `custom-${i}` })
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
return list
|
|
73
|
+
}, [dataColorValues, dataColorMappings])
|
|
74
|
+
|
|
75
|
+
const updateDataColorMapping = (sourceValue: string, color: string) => {
|
|
76
|
+
const nextMappings = [...dataColorMappings]
|
|
77
|
+
const existingIndex = nextMappings.findIndex(m => m.sourceValue === sourceValue)
|
|
78
|
+
|
|
79
|
+
if (!color) {
|
|
80
|
+
if (existingIndex > -1) {
|
|
81
|
+
nextMappings.splice(existingIndex, 1)
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
const nextMapping = { sourceValue, color }
|
|
85
|
+
if (existingIndex > -1) {
|
|
86
|
+
nextMappings[existingIndex] = nextMapping
|
|
87
|
+
} else {
|
|
88
|
+
nextMappings.push(nextMapping)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
updateConfig({
|
|
93
|
+
...config,
|
|
94
|
+
dataColors: {
|
|
95
|
+
...config.dataColors,
|
|
96
|
+
mappings: nextMappings
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const addCustomDataColorMapping = () => {
|
|
102
|
+
const nextMappings = [...dataColorMappings, { sourceValue: '', color: DATA_COLOR_PRESETS[0] }]
|
|
103
|
+
updateConfig({
|
|
104
|
+
...config,
|
|
105
|
+
dataColors: {
|
|
106
|
+
...config.dataColors,
|
|
107
|
+
mappings: nextMappings
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const updateDataColorMappingValue = (oldValue: string, newValue: string) => {
|
|
113
|
+
const nextMappings = dataColorMappings.map(m => (m.sourceValue === oldValue ? { ...m, sourceValue: newValue } : m))
|
|
114
|
+
updateConfig({
|
|
115
|
+
...config,
|
|
116
|
+
dataColors: {
|
|
117
|
+
...config.dataColors,
|
|
118
|
+
mappings: nextMappings
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const removeDataColorMapping = (sourceValue: string) => {
|
|
124
|
+
const nextMappings = dataColorMappings.filter(m => m.sourceValue !== sourceValue)
|
|
125
|
+
updateConfig({
|
|
126
|
+
...config,
|
|
127
|
+
dataColors: {
|
|
128
|
+
...config.dataColors,
|
|
129
|
+
mappings: nextMappings
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const handleStyleTreatmentChange = (value: string) => {
|
|
135
|
+
const useTp5Treatment = value === 'tp5'
|
|
136
|
+
updateConfig({
|
|
137
|
+
...config,
|
|
138
|
+
visual: {
|
|
139
|
+
...config.visual,
|
|
140
|
+
tp5Treatment: useTp5Treatment,
|
|
141
|
+
border: useTp5Treatment ? false : config.visual?.border,
|
|
142
|
+
borderColorTheme: useTp5Treatment ? false : config.visual?.borderColorTheme,
|
|
143
|
+
accent: useTp5Treatment ? false : config.visual?.accent
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
}
|
|
32
147
|
|
|
33
148
|
const textAreaInEditorContainer = useRef(null)
|
|
34
149
|
|
|
@@ -58,6 +173,17 @@ const EditorPanel: React.FC<MarkupIncludeEditorPanelProps> = ({ datasets }) => {
|
|
|
58
173
|
{() => (
|
|
59
174
|
<Accordion>
|
|
60
175
|
<Accordion.Section title='General'>
|
|
176
|
+
<Select
|
|
177
|
+
value={contentEditor?.style || 'default'}
|
|
178
|
+
section='contentEditor'
|
|
179
|
+
fieldName='style'
|
|
180
|
+
label='Style'
|
|
181
|
+
updateField={updateField}
|
|
182
|
+
options={[
|
|
183
|
+
{ value: 'default', label: 'Default' },
|
|
184
|
+
{ value: 'tp5', label: 'TP5' }
|
|
185
|
+
]}
|
|
186
|
+
/>
|
|
61
187
|
<TextField
|
|
62
188
|
value={title || ''}
|
|
63
189
|
section='contentEditor'
|
|
@@ -66,16 +192,42 @@ const EditorPanel: React.FC<MarkupIncludeEditorPanelProps> = ({ datasets }) => {
|
|
|
66
192
|
placeholder='Markup Include Title'
|
|
67
193
|
updateField={updateField}
|
|
68
194
|
/>
|
|
195
|
+
{!isTp5Style && (
|
|
196
|
+
<Select
|
|
197
|
+
value={contentEditor?.titleStyle || 'small'}
|
|
198
|
+
section='contentEditor'
|
|
199
|
+
fieldName='titleStyle'
|
|
200
|
+
label='Title Style'
|
|
201
|
+
updateField={updateField}
|
|
202
|
+
options={[
|
|
203
|
+
{ value: 'small', label: 'Small (h3)' },
|
|
204
|
+
{ value: 'large', label: 'Large (h2)' },
|
|
205
|
+
{ value: 'legacy', label: 'Legacy' }
|
|
206
|
+
]}
|
|
207
|
+
tooltip={
|
|
208
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
209
|
+
<Tooltip.Target>
|
|
210
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
211
|
+
</Tooltip.Target>
|
|
212
|
+
<Tooltip.Content>
|
|
213
|
+
<p>Choose the visual style for the title.</p>
|
|
214
|
+
<p>
|
|
215
|
+
Consider heading order on your page when selecting the title style. For 508 reasons, ensure your
|
|
216
|
+
page follows a proper heading order.
|
|
217
|
+
</p>
|
|
218
|
+
</Tooltip.Content>
|
|
219
|
+
</Tooltip>
|
|
220
|
+
}
|
|
221
|
+
/>
|
|
222
|
+
)}
|
|
69
223
|
<Select
|
|
70
|
-
value={
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
label='Title Style'
|
|
224
|
+
value={config.locale}
|
|
225
|
+
fieldName='locale'
|
|
226
|
+
label='Language for dates and numbers'
|
|
74
227
|
updateField={updateField}
|
|
75
228
|
options={[
|
|
76
|
-
{ value: '
|
|
77
|
-
{ value: '
|
|
78
|
-
{ value: 'legacy', label: 'Legacy' }
|
|
229
|
+
{ value: 'en-US', label: 'English (en-US)' },
|
|
230
|
+
{ value: 'es-MX', label: 'Spanish (es-MX)' }
|
|
79
231
|
]}
|
|
80
232
|
tooltip={
|
|
81
233
|
<Tooltip style={{ textTransform: 'none' }}>
|
|
@@ -83,10 +235,9 @@ const EditorPanel: React.FC<MarkupIncludeEditorPanelProps> = ({ datasets }) => {
|
|
|
83
235
|
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
84
236
|
</Tooltip.Target>
|
|
85
237
|
<Tooltip.Content>
|
|
86
|
-
<p>Choose the visual style for the title.</p>
|
|
87
238
|
<p>
|
|
88
|
-
|
|
89
|
-
|
|
239
|
+
Change the language (locale) for this visualization to alter the way dates and numbers are
|
|
240
|
+
formatted.
|
|
90
241
|
</p>
|
|
91
242
|
</Tooltip.Content>
|
|
92
243
|
</Tooltip>
|
|
@@ -131,8 +282,111 @@ const EditorPanel: React.FC<MarkupIncludeEditorPanelProps> = ({ datasets }) => {
|
|
|
131
282
|
</div>
|
|
132
283
|
</Accordion.Section>
|
|
133
284
|
<Accordion.Section title='Visual'>
|
|
134
|
-
|
|
285
|
+
{!isTp5Style && (
|
|
286
|
+
<HeaderThemeSelector
|
|
287
|
+
selectedTheme={config.theme}
|
|
288
|
+
onThemeSelect={theme => updateConfig({ ...config, theme })}
|
|
289
|
+
/>
|
|
290
|
+
)}
|
|
291
|
+
{isTp5Style ? (
|
|
292
|
+
<CheckBox
|
|
293
|
+
value={visual?.whiteBackground}
|
|
294
|
+
section='visual'
|
|
295
|
+
fieldName='whiteBackground'
|
|
296
|
+
label='Use White Background Style'
|
|
297
|
+
updateField={updateField}
|
|
298
|
+
/>
|
|
299
|
+
) : (
|
|
300
|
+
<StyleTreatmentSection
|
|
301
|
+
styleTreatment={styleTreatment}
|
|
302
|
+
onStyleTreatmentChange={handleStyleTreatmentChange}
|
|
303
|
+
showStyleTreatment={false}
|
|
304
|
+
border={config.visual?.border}
|
|
305
|
+
borderColorTheme={config.visual?.borderColorTheme}
|
|
306
|
+
accent={config.visual?.accent}
|
|
307
|
+
background={config.visual?.background}
|
|
308
|
+
hideBackgroundColor={config.visual?.hideBackgroundColor}
|
|
309
|
+
showBackground
|
|
310
|
+
showHideBackgroundColor
|
|
311
|
+
updateField={updateField}
|
|
312
|
+
/>
|
|
313
|
+
)}
|
|
135
314
|
</Accordion.Section>
|
|
315
|
+
{isTp5Style && (
|
|
316
|
+
<Accordion.Section title='Data-Driven Colors'>
|
|
317
|
+
<Select
|
|
318
|
+
value={config.dataColors?.column || ''}
|
|
319
|
+
section='dataColors'
|
|
320
|
+
fieldName='column'
|
|
321
|
+
label='Color Column'
|
|
322
|
+
updateField={updateField}
|
|
323
|
+
initial='Select'
|
|
324
|
+
options={columns}
|
|
325
|
+
tooltip={
|
|
326
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
327
|
+
<Tooltip.Target>
|
|
328
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
329
|
+
</Tooltip.Target>
|
|
330
|
+
<Tooltip.Content>
|
|
331
|
+
<p>
|
|
332
|
+
Choose a column whose values determine the background color of this visualization. Map each
|
|
333
|
+
value to a color below. Text color adjusts automatically for contrast.
|
|
334
|
+
</p>
|
|
335
|
+
</Tooltip.Content>
|
|
336
|
+
</Tooltip>
|
|
337
|
+
}
|
|
338
|
+
/>
|
|
339
|
+
{config.dataColors?.column && dataColorDisplayList.length > 0 && (
|
|
340
|
+
<div className='mt-2'>
|
|
341
|
+
{dataColorDisplayList.map(({ sourceValue, fromData, key }) => {
|
|
342
|
+
const selectedColor = dataColorMappings.find(m => m.sourceValue === sourceValue)?.color || ''
|
|
343
|
+
|
|
344
|
+
return (
|
|
345
|
+
<div className='cove-accordion__panel-row align-center mb-2' key={key}>
|
|
346
|
+
<div className='cove-accordion__panel-col' style={{ flex: '1 1 0', minWidth: 0 }}>
|
|
347
|
+
{fromData ? (
|
|
348
|
+
sourceValue
|
|
349
|
+
) : (
|
|
350
|
+
<input
|
|
351
|
+
type='text'
|
|
352
|
+
value={sourceValue}
|
|
353
|
+
placeholder='Enter value'
|
|
354
|
+
style={{ width: '100%' }}
|
|
355
|
+
onChange={e => updateDataColorMappingValue(sourceValue, e.target.value)}
|
|
356
|
+
/>
|
|
357
|
+
)}
|
|
358
|
+
</div>
|
|
359
|
+
<div className='cove-accordion__panel-col' style={{ flex: '0 0 4.5rem' }}>
|
|
360
|
+
<DataColorSelector
|
|
361
|
+
value={selectedColor}
|
|
362
|
+
onChange={color => updateDataColorMapping(sourceValue, color)}
|
|
363
|
+
/>
|
|
364
|
+
</div>
|
|
365
|
+
<div className='cove-accordion__panel-col' style={{ flex: '0 0 1.5rem' }}>
|
|
366
|
+
{!fromData && (
|
|
367
|
+
<button
|
|
368
|
+
type='button'
|
|
369
|
+
className='btn btn-danger'
|
|
370
|
+
style={{ padding: '0.15rem 0.45rem', lineHeight: 1 }}
|
|
371
|
+
title='Remove mapping'
|
|
372
|
+
onClick={() => removeDataColorMapping(sourceValue)}
|
|
373
|
+
>
|
|
374
|
+
−
|
|
375
|
+
</button>
|
|
376
|
+
)}
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
)
|
|
380
|
+
})}
|
|
381
|
+
</div>
|
|
382
|
+
)}
|
|
383
|
+
{config.dataColors?.column && (
|
|
384
|
+
<Button type='button' onClick={addCustomDataColorMapping} className='btn btn-primary full-width mt-3'>
|
|
385
|
+
Add Color Mapping
|
|
386
|
+
</Button>
|
|
387
|
+
)}
|
|
388
|
+
</Accordion.Section>
|
|
389
|
+
)}
|
|
136
390
|
{isDashboard && (
|
|
137
391
|
<Accordion.Section title='Footnotes'>
|
|
138
392
|
<FootnotesEditor config={config} updateField={updateField} datasets={datasets} />
|
|
@@ -141,12 +395,13 @@ const EditorPanel: React.FC<MarkupIncludeEditorPanelProps> = ({ datasets }) => {
|
|
|
141
395
|
<Accordion.Section title='Markup Variables'>
|
|
142
396
|
<MarkupVariablesEditor
|
|
143
397
|
markupVariables={config.markupVariables || []}
|
|
144
|
-
data={
|
|
398
|
+
data={markupEditorData}
|
|
145
399
|
datasets={datasets}
|
|
146
400
|
config={config}
|
|
147
401
|
onChange={handleMarkupVariablesChange}
|
|
148
402
|
enableMarkupVariables={config.enableMarkupVariables || false}
|
|
149
403
|
onToggleEnable={handleToggleEnable}
|
|
404
|
+
dataMetadata={config.dataMetadata}
|
|
150
405
|
/>
|
|
151
406
|
</Accordion.Section>
|
|
152
407
|
</Accordion>
|
|
@@ -2,6 +2,7 @@ export default {
|
|
|
2
2
|
contentEditor: {
|
|
3
3
|
inlineHTML: '<strong>Inline HTML</strong>',
|
|
4
4
|
showHeader: true,
|
|
5
|
+
style: 'default',
|
|
5
6
|
srcUrl: '#example',
|
|
6
7
|
title: '',
|
|
7
8
|
titleStyle: 'small',
|
|
@@ -22,7 +23,12 @@ export default {
|
|
|
22
23
|
accent: false,
|
|
23
24
|
background: false,
|
|
24
25
|
hideBackgroundColor: false,
|
|
25
|
-
borderColorTheme: false
|
|
26
|
+
borderColorTheme: false,
|
|
27
|
+
whiteBackground: false
|
|
28
|
+
},
|
|
29
|
+
dataColors: {
|
|
30
|
+
column: '',
|
|
31
|
+
mappings: []
|
|
26
32
|
},
|
|
27
33
|
markupVariables: [],
|
|
28
34
|
enableMarkupVariables: false
|
package/src/scss/main.scss
CHANGED
|
@@ -1,17 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
.cove-component__content-wrap {
|
|
3
|
-
padding: 0;
|
|
1
|
+
@import '@cdc/core/styles/layout/wrapper-padding';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
.cove-visualization.type-markup-include {
|
|
4
|
+
.cove-visualization__body {
|
|
5
|
+
@include cove-visualization-body-padding;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.markup-include-component--tp5 {
|
|
9
|
+
padding: 0 !important;
|
|
10
|
+
border: none !important;
|
|
11
|
+
background: none !important;
|
|
12
|
+
container-type: inline-size;
|
|
13
|
+
|
|
14
|
+
.markup-include-tp5 {
|
|
15
|
+
gap: 0.7rem;
|
|
16
|
+
box-shadow: 0 2px 4px rgb(159 159 159 / 10%);
|
|
17
|
+
border: 1px solid var(--colors-cyan-15, #dff2f6) !important;
|
|
18
|
+
margin: 0 !important;
|
|
19
|
+
padding: 1.25rem;
|
|
20
|
+
border-radius: 0.25rem;
|
|
21
|
+
background-color: var(--colors-cyan-10, #eff9fa);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.cdc-callout__heading {
|
|
25
|
+
width: 100%;
|
|
26
|
+
font-size: 1.1rem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.cdc-callout__body {
|
|
30
|
+
flex-wrap: nowrap;
|
|
31
|
+
flex: auto;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@container (max-width: 576px) {
|
|
35
|
+
.cdc-callout__body {
|
|
36
|
+
flex-wrap: wrap;
|
|
37
|
+
}
|
|
8
38
|
}
|
|
9
39
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
padding-right: 1rem;
|
|
40
|
+
.cdc-callout__content {
|
|
41
|
+
font-size: 1rem;
|
|
13
42
|
}
|
|
14
43
|
|
|
44
|
+
.cdc-callout--data-color {
|
|
45
|
+
.cdc-callout__heading,
|
|
46
|
+
.cdc-callout__content {
|
|
47
|
+
color: inherit;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&.white-background-style {
|
|
52
|
+
.markup-include-tp5 {
|
|
53
|
+
border: 1px solid #009ec1 !important;
|
|
54
|
+
background: transparent;
|
|
55
|
+
box-shadow: 0 2px 4px rgb(159 159 159 / 10%);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.cdc-callout--data-color {
|
|
59
|
+
border: none !important;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&.white-background-style.display-border {
|
|
64
|
+
.markup-include-tp5 {
|
|
65
|
+
box-shadow: 0 2px 4px rgb(159 159 159 / 10%);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.cove-visualization__body-wrap,
|
|
70
|
+
.cove-visualization__content-section {
|
|
71
|
+
padding: 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.cove-visualization__body-wrap {
|
|
76
|
+
@include cove-visualization-body-wrap-inline-padding;
|
|
77
|
+
|
|
15
78
|
h1,
|
|
16
79
|
h2,
|
|
17
80
|
h3 {
|
|
@@ -22,9 +85,16 @@
|
|
|
22
85
|
margin-top: auto;
|
|
23
86
|
margin-bottom: auto;
|
|
24
87
|
}
|
|
88
|
+
|
|
89
|
+
// keep markup include images inlines on dashboards
|
|
90
|
+
// e.g. requested to help here: https://www.cdc.gov/fluview/surveillance/2026-week-10.html
|
|
91
|
+
// needed for backwards compatibility with existing dashboards, but also to prevent any future issues with images in markup include visualizations being forced to display as blocks
|
|
92
|
+
span > img {
|
|
93
|
+
display: inline;
|
|
94
|
+
}
|
|
25
95
|
}
|
|
26
96
|
|
|
27
|
-
.cove-
|
|
97
|
+
.cove-visualization__body.component--hide-background-color {
|
|
28
98
|
background: transparent;
|
|
29
99
|
}
|
|
30
100
|
|
|
@@ -47,6 +117,7 @@
|
|
|
47
117
|
float: left;
|
|
48
118
|
}
|
|
49
119
|
}
|
|
120
|
+
|
|
50
121
|
.cove-editor .cove-editor__content {
|
|
51
122
|
padding-left: 350px;
|
|
52
123
|
display: flex;
|