@cdc/markup-include 4.26.3 → 4.26.5
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 +19331 -13297
- package/examples/minimal-example.json +11 -0
- package/package.json +4 -4
- package/src/CdcMarkupInclude.tsx +159 -40
- package/src/ConfigContext.ts +1 -0
- package/src/_stories/MarkupInclude.Editor.stories.tsx +46 -1
- package/src/_stories/MarkupInclude.smoke.stories.tsx +33 -0
- package/src/_stories/MarkupInclude.stories.tsx +121 -0
- package/src/cdcMarkupInclude.style.css +9 -6
- package/src/components/EditorPanel/EditorPanel.tsx +242 -47
- package/src/data/initial-state.js +7 -1
- package/src/index.tsx +6 -3
- package/src/scss/main.scss +64 -2
- package/src/test/CdcMarkupInclude.test.jsx +172 -1
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/markup-include",
|
|
3
|
-
"version": "4.26.
|
|
3
|
+
"version": "4.26.5",
|
|
4
4
|
"description": "React component for displaying HTML content from an outside link",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Rob Shelnutt <rob@blackairplane.com>",
|
|
7
7
|
"bugs": "https://github.com/CDCgov/cdc-open-viz/issues",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@cdc/core": "^4.26.
|
|
9
|
+
"@cdc/core": "^4.26.5",
|
|
10
10
|
"axios": "^1.13.2",
|
|
11
|
-
"dompurify": "^3.
|
|
11
|
+
"dompurify": "^3.4.0",
|
|
12
12
|
"lodash": "^4.17.23",
|
|
13
13
|
"react-accessible-accordion": "^5.0.1"
|
|
14
14
|
},
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"vite-plugin-css-injected-by-js": "^2.4.0",
|
|
20
20
|
"vite-plugin-svgr": "^4.2.0"
|
|
21
21
|
},
|
|
22
|
-
"gitHead": "
|
|
22
|
+
"gitHead": "61c025165d96b45a6002c34582c5a622a9d865a9",
|
|
23
23
|
"homepage": "https://github.com/CDCgov/cdc-open-viz#readme",
|
|
24
24
|
"main": "dist/cdcmarkupinclude",
|
|
25
25
|
"moduleName": "CdcMarkupInclude",
|
package/src/CdcMarkupInclude.tsx
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { useEffect, useCallback, useRef, useReducer, useMemo } from 'react'
|
|
2
2
|
// external
|
|
3
3
|
import DOMPurify from 'dompurify'
|
|
4
|
-
import
|
|
4
|
+
import parse from 'html-react-parser'
|
|
5
5
|
|
|
6
6
|
// cdc
|
|
7
7
|
import { MarkupIncludeConfig } from '@cdc/core/types/MarkupInclude'
|
|
8
8
|
import { publish } from '@cdc/core/helpers/events'
|
|
9
9
|
import { processMarkupVariables } from '@cdc/core/helpers/markupProcessor'
|
|
10
10
|
import { addValuesToFilters } from '@cdc/core/helpers/addValuesToFilters'
|
|
11
|
+
import { hasVisibleVizFilters } from '@cdc/core/helpers/filterVisibility'
|
|
12
|
+
import { resolveDataColor } from '@cdc/core/helpers/dataColors'
|
|
11
13
|
import ConfigContext from './ConfigContext'
|
|
12
14
|
import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
|
|
13
15
|
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
@@ -30,6 +32,7 @@ type CdcMarkupIncludeProps = {
|
|
|
30
32
|
datasets: Datasets
|
|
31
33
|
isDashboard: boolean
|
|
32
34
|
isEditor: boolean
|
|
35
|
+
rawData?: any[]
|
|
33
36
|
setConfig: any
|
|
34
37
|
interactionLabel?: string
|
|
35
38
|
}
|
|
@@ -45,6 +48,7 @@ const CdcMarkupInclude: React.FC<CdcMarkupIncludeProps> = ({
|
|
|
45
48
|
datasets,
|
|
46
49
|
isDashboard = true,
|
|
47
50
|
isEditor = false,
|
|
51
|
+
rawData,
|
|
48
52
|
setConfig: setParentConfig,
|
|
49
53
|
interactionLabel = 'no link provided'
|
|
50
54
|
}) => {
|
|
@@ -64,7 +68,7 @@ const CdcMarkupInclude: React.FC<CdcMarkupIncludeProps> = ({
|
|
|
64
68
|
// Custom States
|
|
65
69
|
const container = useRef()
|
|
66
70
|
|
|
67
|
-
const { innerContainerClasses, contentClasses } = useDataVizClasses(config || {})
|
|
71
|
+
const { innerContainerClasses, contentClasses: rawContentClasses } = useDataVizClasses(config || {})
|
|
68
72
|
const { contentEditor, theme, visual } = config || {}
|
|
69
73
|
const {
|
|
70
74
|
showNoDataMessage,
|
|
@@ -76,9 +80,45 @@ const CdcMarkupInclude: React.FC<CdcMarkupIncludeProps> = ({
|
|
|
76
80
|
|
|
77
81
|
// Support markupVariables at root level or inside contentEditor
|
|
78
82
|
const markupVariables = config?.markupVariables || contentEditorMarkupVariables || []
|
|
83
|
+
const editorData = useMemo(() => {
|
|
84
|
+
if (isDashboard && isEditor && Array.isArray(rawData) && rawData.length) {
|
|
85
|
+
return rawData
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const assignedDatasetData = config?.dataKey ? datasets?.[config.dataKey]?.data : undefined
|
|
89
|
+
if (Array.isArray(assignedDatasetData) && assignedDatasetData.length) {
|
|
90
|
+
return assignedDatasetData
|
|
91
|
+
}
|
|
79
92
|
|
|
80
|
-
|
|
93
|
+
return data || []
|
|
94
|
+
}, [config?.dataKey, data, datasets, isDashboard, isEditor, rawData])
|
|
81
95
|
|
|
96
|
+
const { inlineHTML, srcUrl, title, useInlineHTML, style: contentStyle } = contentEditor || {}
|
|
97
|
+
const markupIncludeStyle = contentStyle || 'default'
|
|
98
|
+
const isTp5Style = markupIncludeStyle === 'tp5'
|
|
99
|
+
|
|
100
|
+
const dataColorResolution = useMemo(() => {
|
|
101
|
+
const dataArr = Array.isArray(data) ? data : []
|
|
102
|
+
return resolveDataColor({
|
|
103
|
+
data: dataArr,
|
|
104
|
+
dataColors: config?.dataColors
|
|
105
|
+
})
|
|
106
|
+
}, [data, config?.dataColors])
|
|
107
|
+
|
|
108
|
+
const contentClasses = isTp5Style
|
|
109
|
+
? rawContentClasses.filter(
|
|
110
|
+
cls =>
|
|
111
|
+
cls !== 'component--has-accent' &&
|
|
112
|
+
cls !== 'component--has-background' &&
|
|
113
|
+
cls !== 'component--hide-background-color' &&
|
|
114
|
+
cls !== 'component--has-border-color-theme'
|
|
115
|
+
)
|
|
116
|
+
: rawContentClasses
|
|
117
|
+
|
|
118
|
+
const shouldApplyTopPadding =
|
|
119
|
+
!isTp5Style &&
|
|
120
|
+
(visual?.border || visual?.background || (contentEditor?.title && contentEditor?.titleStyle === 'legacy'))
|
|
121
|
+
const shouldApplySidePadding = !isTp5Style && (visual?.border || visual?.accent || visual?.background)
|
|
82
122
|
// Default Functions
|
|
83
123
|
const updateConfig = newConfig => {
|
|
84
124
|
Object.keys(defaults).forEach(key => {
|
|
@@ -161,20 +201,18 @@ const CdcMarkupInclude: React.FC<CdcMarkupIncludeProps> = ({
|
|
|
161
201
|
dispatch({ type: 'SET_URL_MARKUP', payload })
|
|
162
202
|
} else {
|
|
163
203
|
try {
|
|
164
|
-
await
|
|
165
|
-
|
|
166
|
-
|
|
204
|
+
const res = await fetch(srcUrl)
|
|
205
|
+
if (!res.ok) {
|
|
206
|
+
dispatch({ type: 'SET_MARKUP_ERROR', payload: res.status })
|
|
207
|
+
dispatch({ type: 'SET_URL_MARKUP', payload: '' })
|
|
208
|
+
} else {
|
|
209
|
+
const data = await res.text()
|
|
210
|
+
if (data) {
|
|
211
|
+
dispatch({ type: 'SET_URL_MARKUP', payload: data })
|
|
167
212
|
}
|
|
168
|
-
})
|
|
169
|
-
} catch (err) {
|
|
170
|
-
if (err.response) {
|
|
171
|
-
// Response with error
|
|
172
|
-
dispatch({ type: 'SET_MARKUP_ERROR', payload: err.response.status })
|
|
173
|
-
} else if (err.request) {
|
|
174
|
-
// No response received
|
|
175
|
-
dispatch({ type: 'SET_MARKUP_ERROR', payload: 200 })
|
|
176
213
|
}
|
|
177
|
-
|
|
214
|
+
} catch {
|
|
215
|
+
dispatch({ type: 'SET_MARKUP_ERROR', payload: 200 })
|
|
178
216
|
dispatch({ type: 'SET_URL_MARKUP', payload: '' })
|
|
179
217
|
}
|
|
180
218
|
}
|
|
@@ -265,13 +303,48 @@ const CdcMarkupInclude: React.FC<CdcMarkupIncludeProps> = ({
|
|
|
265
303
|
const hideMarkupInclude = processedMarkup.shouldHideSection
|
|
266
304
|
const _showNoDataMessage = processedMarkup.shouldShowNoDataMessage
|
|
267
305
|
|
|
306
|
+
const processedTitle = useMemo(() => {
|
|
307
|
+
if (!config?.enableMarkupVariables || !markupVariables?.length || !title) return title
|
|
308
|
+
return processMarkupVariables(title, data || [], markupVariables, {
|
|
309
|
+
isEditor,
|
|
310
|
+
showNoDataMessage,
|
|
311
|
+
allowHideSection,
|
|
312
|
+
filters: config?.filters || [],
|
|
313
|
+
datasets,
|
|
314
|
+
configDataKey: config?.dataKey,
|
|
315
|
+
locale: config?.locale,
|
|
316
|
+
dataMetadata: config?.dataMetadata
|
|
317
|
+
}).processedContent
|
|
318
|
+
}, [
|
|
319
|
+
title,
|
|
320
|
+
data,
|
|
321
|
+
markupVariables,
|
|
322
|
+
config?.enableMarkupVariables,
|
|
323
|
+
config?.filters,
|
|
324
|
+
config?.dataKey,
|
|
325
|
+
config?.locale,
|
|
326
|
+
config?.dataMetadata,
|
|
327
|
+
isEditor,
|
|
328
|
+
showNoDataMessage,
|
|
329
|
+
allowHideSection,
|
|
330
|
+
datasets
|
|
331
|
+
])
|
|
332
|
+
|
|
268
333
|
if (loading === false) {
|
|
334
|
+
const hasTp5Title = processedTitle && processedTitle.trim()
|
|
269
335
|
content = !hideMarkupInclude && (
|
|
270
336
|
<VisualizationContent
|
|
271
337
|
innerClassName={`markup-include-content-container ${innerContainerClasses.join(' ')}`.trim()}
|
|
272
|
-
bodyClassName={`markup-include-component ${contentClasses.join(' ')}
|
|
273
|
-
|
|
274
|
-
|
|
338
|
+
bodyClassName={`markup-include-component ${contentClasses.join(' ')}${
|
|
339
|
+
isTp5Style ? ' markup-include-component--tp5' : ''
|
|
340
|
+
}${isTp5Style && visual?.whiteBackground ? ' white-background-style' : ''}${
|
|
341
|
+
isTp5Style && visual?.whiteBackground && visual?.border ? ' display-border' : ''
|
|
342
|
+
}`.trim()}
|
|
343
|
+
bodyWrapClassName={`${isTp5Style ? 'markup-include-body-wrap--tp5' : ''}${
|
|
344
|
+
shouldApplyTopPadding ? ' has-top-padding' : ''
|
|
345
|
+
}${shouldApplySidePadding ? ' has-side-padding' : ''}`.trim()}
|
|
346
|
+
filters={
|
|
347
|
+
hasVisibleVizFilters(config.filters) ? (
|
|
275
348
|
<Filters
|
|
276
349
|
config={config}
|
|
277
350
|
setFilters={setFilters}
|
|
@@ -279,41 +352,81 @@ const CdcMarkupInclude: React.FC<CdcMarkupIncludeProps> = ({
|
|
|
279
352
|
dimensions={[0, 0]}
|
|
280
353
|
interactionLabel={interactionLabel || 'markup-include'}
|
|
281
354
|
/>
|
|
282
|
-
) :
|
|
355
|
+
) : undefined
|
|
283
356
|
}
|
|
284
357
|
header={
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
358
|
+
!isTp5Style ? (
|
|
359
|
+
<Title
|
|
360
|
+
title={processedTitle}
|
|
361
|
+
isDashboard={isDashboard}
|
|
362
|
+
titleStyle={contentEditor?.titleStyle}
|
|
363
|
+
config={config}
|
|
364
|
+
classes={[`${theme}`, 'mb-0']}
|
|
365
|
+
noContent={!sanitizedHTML}
|
|
366
|
+
/>
|
|
367
|
+
) : null
|
|
293
368
|
}
|
|
294
369
|
footer={
|
|
295
370
|
<FootnotesStandAlone
|
|
296
|
-
config={
|
|
371
|
+
config={config?.footnotes}
|
|
297
372
|
filters={config?.filters || []}
|
|
298
373
|
markupVariables={markupVariables}
|
|
299
374
|
enableMarkupVariables={config?.enableMarkupVariables}
|
|
300
375
|
data={data}
|
|
301
376
|
dataMetadata={config?.dataMetadata}
|
|
377
|
+
footerClassName={isTp5Style ? 'mt-3' : undefined}
|
|
302
378
|
/>
|
|
303
379
|
}
|
|
304
380
|
>
|
|
305
|
-
{
|
|
306
|
-
<div
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
381
|
+
{isTp5Style ? (
|
|
382
|
+
<div
|
|
383
|
+
className={`markup-include-tp5 cdc-callout d-flex flex-column h-100 ${
|
|
384
|
+
dataColorResolution.state === 'resolved' ? 'cdc-callout--data-color' : ''
|
|
385
|
+
}`}
|
|
386
|
+
style={
|
|
387
|
+
dataColorResolution.state === 'resolved'
|
|
388
|
+
? { backgroundColor: dataColorResolution.color, color: dataColorResolution.textColor }
|
|
389
|
+
: undefined
|
|
390
|
+
}
|
|
391
|
+
>
|
|
392
|
+
{hasTp5Title && (
|
|
393
|
+
<h3 className='cdc-callout__heading cove-prose fw-bold flex-shrink-0 d-flex align-items-start'>
|
|
394
|
+
<span>{parse(processedTitle.trim())}</span>
|
|
395
|
+
</h3>
|
|
396
|
+
)}
|
|
397
|
+
<div className='cdc-callout__body d-flex flex-row align-content-start flex-grow-1'>
|
|
398
|
+
<div className='cdc-callout__content flex-grow-1 d-flex flex-column min-w-0'>
|
|
399
|
+
{_showNoDataMessage && (
|
|
400
|
+
<div className='no-data-message'>
|
|
401
|
+
<p>{`${noDataMessageText}`}</p>
|
|
402
|
+
</div>
|
|
403
|
+
)}
|
|
404
|
+
{!markupError && !_showNoDataMessage && (
|
|
405
|
+
<div id={scopeId}>
|
|
406
|
+
{scopedCSS && <style>{scopedCSS}</style>}
|
|
407
|
+
<div className='cove-prose' dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />
|
|
408
|
+
</div>
|
|
409
|
+
)}
|
|
410
|
+
{markupError && srcUrl && !_showNoDataMessage && <div className='warning'>{errorMessage}</div>}
|
|
411
|
+
</div>
|
|
412
|
+
</div>
|
|
314
413
|
</div>
|
|
414
|
+
) : (
|
|
415
|
+
<>
|
|
416
|
+
{_showNoDataMessage && (
|
|
417
|
+
<div className='no-data-message'>
|
|
418
|
+
<p>{`${noDataMessageText}`}</p>
|
|
419
|
+
</div>
|
|
420
|
+
)}
|
|
421
|
+
{!markupError && !_showNoDataMessage && (
|
|
422
|
+
<div id={scopeId}>
|
|
423
|
+
{scopedCSS && <style>{scopedCSS}</style>}
|
|
424
|
+
<div className='cove-prose' dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />
|
|
425
|
+
</div>
|
|
426
|
+
)}
|
|
427
|
+
{markupError && srcUrl && !_showNoDataMessage && <div className='warning'>{errorMessage}</div>}
|
|
428
|
+
</>
|
|
315
429
|
)}
|
|
316
|
-
{markupError && srcUrl && !_showNoDataMessage && <div className='warning'>{errorMessage}</div>}
|
|
317
430
|
</VisualizationContent>
|
|
318
431
|
)
|
|
319
432
|
}
|
|
@@ -331,9 +444,15 @@ const CdcMarkupInclude: React.FC<CdcMarkupIncludeProps> = ({
|
|
|
331
444
|
|
|
332
445
|
return (
|
|
333
446
|
<ErrorBoundary component='CdcMarkupInclude'>
|
|
334
|
-
<ConfigContext.Provider
|
|
447
|
+
<ConfigContext.Provider
|
|
448
|
+
value={{ config, updateConfig, loading, data: data, editorData, setParentConfig, isDashboard }}
|
|
449
|
+
>
|
|
335
450
|
{!config?.newViz && config?.runtime && config?.runtime.editorErrorMessage && <Error />}
|
|
336
|
-
<VisualizationContainer
|
|
451
|
+
<VisualizationContainer
|
|
452
|
+
config={config as any}
|
|
453
|
+
isEditor={isEditor}
|
|
454
|
+
editorPanel={<EditorPanel datasets={datasets} />}
|
|
455
|
+
>
|
|
337
456
|
{content}
|
|
338
457
|
</VisualizationContainer>
|
|
339
458
|
</ConfigContext.Provider>
|
package/src/ConfigContext.ts
CHANGED
|
@@ -75,8 +75,10 @@ const testConfig = {
|
|
|
75
75
|
contentEditor: {
|
|
76
76
|
inlineHTML: '<h2>Test Markup Include</h2><p>{{test_variable}}</p>',
|
|
77
77
|
showHeader: true,
|
|
78
|
+
style: 'default',
|
|
78
79
|
srcUrl: '',
|
|
79
80
|
title: 'Test Markup Include Title',
|
|
81
|
+
titleStyle: 'small',
|
|
80
82
|
useInlineHTML: true,
|
|
81
83
|
showNoDataMessage: false,
|
|
82
84
|
noDataMessageText: 'No data available'
|
|
@@ -89,7 +91,6 @@ const testConfig = {
|
|
|
89
91
|
legend: {},
|
|
90
92
|
newViz: true,
|
|
91
93
|
theme: 'theme-blue',
|
|
92
|
-
titleStyle: 'small',
|
|
93
94
|
showTitle: true,
|
|
94
95
|
type: 'markup-include',
|
|
95
96
|
visual: {
|
|
@@ -154,6 +155,50 @@ export const GeneralSectionTests: Story = {
|
|
|
154
155
|
const headerElement = modernHeader || legacyHeader
|
|
155
156
|
expect(headerElement).toBeTruthy()
|
|
156
157
|
expect(headerElement!.textContent?.trim()).toBe('Updated Markup Include Title E2E')
|
|
158
|
+
|
|
159
|
+
// ============================================================================
|
|
160
|
+
// TEST 2: Switch to TP5 Style
|
|
161
|
+
// Expectation: TP5 container appears and legacy title wrapper is removed
|
|
162
|
+
// ============================================================================
|
|
163
|
+
const styleSelect = canvasElement.querySelector('select[name="style"]') as HTMLSelectElement
|
|
164
|
+
expect(styleSelect).toBeTruthy()
|
|
165
|
+
|
|
166
|
+
await performAndAssert(
|
|
167
|
+
'Style Change to TP5',
|
|
168
|
+
() => ({
|
|
169
|
+
styleValue: styleSelect.value,
|
|
170
|
+
hasTp5Container: !!canvasElement.querySelector('.markup-include-component--tp5')
|
|
171
|
+
}),
|
|
172
|
+
async () => {
|
|
173
|
+
await userEvent.selectOptions(styleSelect, 'tp5')
|
|
174
|
+
},
|
|
175
|
+
(before, after) =>
|
|
176
|
+
before.styleValue !== after.styleValue &&
|
|
177
|
+
after.styleValue === 'tp5' &&
|
|
178
|
+
before.hasTp5Container !== after.hasTp5Container
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// TEST 3: TP5 title visibility
|
|
183
|
+
// Expectation: TP5 heading hidden when title is empty, shown when title has value
|
|
184
|
+
// ============================================================================
|
|
185
|
+
await performAndAssert(
|
|
186
|
+
'TP5 Title Hidden When Empty',
|
|
187
|
+
() => !!canvasElement.querySelector('.markup-include-tp5 .cdc-callout__heading span'),
|
|
188
|
+
async () => {
|
|
189
|
+
await userEvent.clear(titleInput)
|
|
190
|
+
},
|
|
191
|
+
(before, after) => before !== after && after === false
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
await performAndAssert(
|
|
195
|
+
'TP5 Title Shows With Text',
|
|
196
|
+
() => canvasElement.querySelector('.markup-include-tp5 .cdc-callout__heading span')?.textContent?.trim() || '',
|
|
197
|
+
async () => {
|
|
198
|
+
await userEvent.type(titleInput, 'TP5 Title Test')
|
|
199
|
+
},
|
|
200
|
+
(before, after) => before !== after && after === 'TP5 Title Test'
|
|
201
|
+
)
|
|
157
202
|
}
|
|
158
203
|
}
|
|
159
204
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { expect } from 'storybook/test'
|
|
3
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
4
|
+
import CdcMarkupInclude from '../CdcMarkupInclude'
|
|
5
|
+
import MinimalExampleConfig from '../../examples/minimal-example.json'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof CdcMarkupInclude> = {
|
|
8
|
+
title: 'Components/Pages/Markup Include',
|
|
9
|
+
component: CdcMarkupInclude
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default meta
|
|
13
|
+
type Story = StoryObj<typeof CdcMarkupInclude>
|
|
14
|
+
|
|
15
|
+
export const Markup_Include_Minimal_Config: Story = {
|
|
16
|
+
args: {
|
|
17
|
+
config: MinimalExampleConfig,
|
|
18
|
+
isEditor: false
|
|
19
|
+
},
|
|
20
|
+
parameters: {
|
|
21
|
+
docs: {
|
|
22
|
+
description: {
|
|
23
|
+
story:
|
|
24
|
+
'Minimum working consumer config. This story validates the source-of-truth minimal example used by the package README and CONFIG reference.'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
play: async ({ canvasElement }) => {
|
|
29
|
+
await assertVisualizationRendered(canvasElement)
|
|
30
|
+
expect(canvasElement.textContent).toContain('Markup Include')
|
|
31
|
+
expect(canvasElement.textContent).toContain('Markup include minimum example.')
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -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,66 @@ 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
|
+
|
|
139
|
+
export const Bootstrap_Grid_Mockup: Story = {
|
|
140
|
+
args: {
|
|
141
|
+
config: {
|
|
142
|
+
...primary,
|
|
143
|
+
contentEditor: {
|
|
144
|
+
...primary.contentEditor,
|
|
145
|
+
showHeader: true,
|
|
146
|
+
title: 'Bootstrap grid include mock',
|
|
147
|
+
useInlineHTML: true,
|
|
148
|
+
inlineHTML: `
|
|
149
|
+
<div class="row">
|
|
150
|
+
<div class="col-8">
|
|
151
|
+
<p><strong>col-8</strong> block inside markup include content.</p>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="col-3">
|
|
154
|
+
<p><strong>col-3</strong> block inside markup include content.</p>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
`
|
|
158
|
+
}
|
|
159
|
+
} as any,
|
|
160
|
+
isEditor: false
|
|
161
|
+
},
|
|
162
|
+
play: async ({ canvasElement }) => {
|
|
163
|
+
await assertVisualizationRendered(canvasElement)
|
|
164
|
+
|
|
165
|
+
const shell = canvasElement.querySelector('.cove-visualization.type-markup-include')
|
|
166
|
+
const contentSection = canvasElement.querySelector('.cove-visualization__content-section')
|
|
167
|
+
const primaryColumn = canvasElement.querySelector('.col-8')
|
|
168
|
+
const secondaryColumn = canvasElement.querySelector('.col-3')
|
|
169
|
+
|
|
170
|
+
expect(shell).toBeTruthy()
|
|
171
|
+
expect(contentSection).toBeTruthy()
|
|
172
|
+
expect(primaryColumn).toBeTruthy()
|
|
173
|
+
expect(secondaryColumn).toBeTruthy()
|
|
174
|
+
expect(primaryColumn?.closest('.cove-visualization__content-section')).toBe(contentSection)
|
|
175
|
+
expect(secondaryColumn?.closest('.cove-visualization__content-section')).toBe(contentSection)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
58
179
|
export default meta
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.cove-visualization.markup-include {
|
|
1
|
+
.cove-visualization.type-markup-include {
|
|
2
2
|
.spacing-wrapper {
|
|
3
3
|
background-color: var(--lightGray) !important;
|
|
4
4
|
}
|
|
@@ -7,14 +7,17 @@
|
|
|
7
7
|
display: none;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
.markup-include-component.cove-visualization__body:not(
|
|
10
|
+
.markup-include-content-container.cove-visualization__inner {
|
|
11
|
+
.markup-include-component.cove-visualization__body:not(
|
|
12
|
+
.component--hide-background-color,
|
|
13
|
+
.component--has-background
|
|
14
|
+
) {
|
|
12
15
|
background-color: white;
|
|
13
16
|
}
|
|
14
17
|
}
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
.cove-visualization.markup-include.is-editor {
|
|
20
|
+
.cove-visualization.type-markup-include.is-editor {
|
|
18
21
|
.cove-tooltip-variable {
|
|
19
22
|
background: var(--orange-tertiary);
|
|
20
23
|
display: inline;
|
|
@@ -32,7 +35,7 @@
|
|
|
32
35
|
z-index: 9999;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
&:hover
|
|
38
|
+
&:hover > .cove-tooltip-value {
|
|
36
39
|
display: block;
|
|
37
40
|
}
|
|
38
41
|
}
|
|
@@ -40,4 +43,4 @@
|
|
|
40
43
|
.cove-markup-include-variable-value {
|
|
41
44
|
display: none !important;
|
|
42
45
|
}
|
|
43
|
-
}
|
|
46
|
+
}
|