@sakura-ui/sakura-ui 0.5.4 → 0.5.6
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/package.json +4 -4
- package/packages/core/package.json +1 -1
- package/packages/core/src/components/Link.tsx +17 -0
- package/packages/core/tests/Link.test.tsx +41 -0
- package/packages/forms/.turbo/turbo-build.log +2 -2
- package/packages/helper/.turbo/turbo-build.log +1 -1
- package/packages/markdown/.turbo/turbo-build.log +5 -5
- package/packages/markdown/README.md +17 -1
- package/packages/markdown/dist/index.cjs.js +110 -43
- package/packages/markdown/dist/index.es.js +862 -744
- package/packages/markdown/dist/types/decorate.d.ts.map +1 -1
- package/packages/markdown/dist/types/marked/directives/image.d.ts +3 -0
- package/packages/markdown/dist/types/marked/directives/image.d.ts.map +1 -0
- package/packages/markdown/dist/types/marked/directives/index.d.ts.map +1 -1
- package/packages/markdown/dist/types/marked/html.d.ts.map +1 -1
- package/packages/markdown/dist/types/marked/registry.d.ts.map +1 -1
- package/packages/markdown/dist/types/sanitize.d.ts.map +1 -1
- package/packages/markdown/package.json +1 -1
- package/packages/markdown/src/decorate.ts +25 -9
- package/packages/markdown/src/marked/directives/image.ts +29 -0
- package/packages/markdown/src/marked/directives/index.ts +2 -0
- package/packages/markdown/src/marked/html.ts +14 -1
- package/packages/markdown/src/marked/registry.ts +1 -0
- package/packages/markdown/src/marked/renderer.ts +3 -3
- package/packages/markdown/src/sanitize.ts +9 -2
- package/packages/markdown/tests/Markdown.test.tsx +17 -0
- package/packages/markdown/tests/__snapshots__/Markdown.test.tsx.snap +36 -31
- package/packages/markdown/tests/image.test.ts +64 -0
- package/packages/markdown/tests/sanitize.test.ts +17 -0
- package/packages/tailwind-theme-plugin/.turbo/turbo-build.log +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorate.d.ts","sourceRoot":"","sources":["../../src/decorate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"decorate.d.ts","sourceRoot":"","sources":["../../src/decorate.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;CACzB,CAAA;AAoED,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAA;CACxB,CAAA;AAID,eAAO,MAAM,QAAQ,GACnB,MAAM,gBAAgB,GAAG,OAAO,EAChC,UAAU,QAAQ,EAClB,SAAS,eAAe,KACvB,cA0EF,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../../src/marked/directives/image.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAmBlD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAQ5D,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/marked/directives/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/marked/directives/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAgBlD;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,iBAAiB,GAAG,SACN,CAAA;AAE1D,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,MAAM,EACN,eAAe,EAChB,MAAM,WAAW,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../src/marked/html.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,MACe,CAAA;AAgB3D;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAS5D,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../src/marked/html.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,MACe,CAAA;AAgB3D;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAS5D,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,CAAA;AAS3D,2DAA2D;AAC3D,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,KAAG,MAUlD,CAAA;AAEb;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,QAAQ,CAAC,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC,EAAE,KAAG,MACE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/marked/registry.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,cAAc,CAAA;AACpC,eAAO,MAAM,IAAI,SAAS,CAAA;AAC1B,eAAO,MAAM,IAAI,SAAS,CAAA;AAE1B,MAAM,MAAM,aAAa,GAAG,OAAO,SAAS,GAAG,OAAO,IAAI,GAAG,OAAO,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/marked/registry.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,cAAc,CAAA;AACpC,eAAO,MAAM,IAAI,SAAS,CAAA;AAC1B,eAAO,MAAM,IAAI,SAAS,CAAA;AAE1B,MAAM,MAAM,aAAa,GAAG,OAAO,SAAS,GAAG,OAAO,IAAI,GAAG,OAAO,IAAI,CAAA;AAuBxE,+EAA+E;AAC/E,eAAO,MAAM,OAAO,QAAiC,CAAA;AAErD,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,EAAE,MAAM,aAAa,KAAG,OACmB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/sanitize.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/sanitize.ts"],"names":[],"mappings":"AAyLA;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,KAAG,gBAQvC,CAAA;AAED,eAAO,MAAM,SAAS;yBAzGM,MAAM,KAAG,MAAM;;CAyGqB,CAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { styles } from '@sakura-ui/core'
|
|
2
|
+
import { treefy } from '@sakura-ui/helper'
|
|
2
3
|
import Slugger from 'github-slugger'
|
|
3
4
|
import { classNames } from './marked/html'
|
|
4
5
|
|
|
@@ -38,7 +39,21 @@ const CLASS_BY_TAG: Record<string, string> = {
|
|
|
38
39
|
|
|
39
40
|
/** Elements a directive rendered are already dressed; leave them be. */
|
|
40
41
|
const isDirectiveOutput = (element: Element) =>
|
|
41
|
-
element.hasAttribute('data-
|
|
42
|
+
element.hasAttribute('data-styled')
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A link that opens elsewhere must not hand the opener to the page it opens.
|
|
46
|
+
* Merged with what the document asked for rather than replacing it, so a
|
|
47
|
+
* `rel="nofollow"` does not take `noopener` away with it.
|
|
48
|
+
*/
|
|
49
|
+
const externalRel = (rel: string | null): string =>
|
|
50
|
+
Array.from(
|
|
51
|
+
new Set([
|
|
52
|
+
'noopener',
|
|
53
|
+
'noreferrer',
|
|
54
|
+
...(rel ?? '').split(/\s+/).filter(Boolean)
|
|
55
|
+
])
|
|
56
|
+
).join(' ')
|
|
42
57
|
|
|
43
58
|
const addClass = (element: Element, value: string) => {
|
|
44
59
|
const merged = classNames(element.getAttribute('class') ?? '', value)
|
|
@@ -53,10 +68,10 @@ const HEADINGS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
|
|
|
53
68
|
* the Icon component does.
|
|
54
69
|
*/
|
|
55
70
|
const appendNewTabIcon = (anchor: Element, document: Document) => {
|
|
56
|
-
if (anchor.querySelector('[data-
|
|
71
|
+
if (anchor.querySelector('[data-new-tab]')) return
|
|
57
72
|
const glyph = document.createElement('span')
|
|
58
73
|
glyph.setAttribute('aria-hidden', 'true')
|
|
59
|
-
glyph.setAttribute('data-
|
|
74
|
+
glyph.setAttribute('data-new-tab', 'true')
|
|
60
75
|
glyph.setAttribute(
|
|
61
76
|
'class',
|
|
62
77
|
classNames(styles.iconSizeStyle[16], styles.iconStyle, 'ml-0.5')
|
|
@@ -131,9 +146,7 @@ export const decorate = (
|
|
|
131
146
|
element.getAttribute('target') === '_blank'
|
|
132
147
|
) {
|
|
133
148
|
element.setAttribute('target', '_blank')
|
|
134
|
-
|
|
135
|
-
// leaves the opened page holding a handle on this one.
|
|
136
|
-
element.setAttribute('rel', 'noopener noreferrer')
|
|
149
|
+
element.setAttribute('rel', externalRel(element.getAttribute('rel')))
|
|
137
150
|
appendNewTabIcon(element, document)
|
|
138
151
|
}
|
|
139
152
|
}
|
|
@@ -145,15 +158,18 @@ export const decorate = (
|
|
|
145
158
|
// A wide table scrolls inside its own box rather than pushing the page.
|
|
146
159
|
for (const table of Array.from(root.querySelectorAll('table'))) {
|
|
147
160
|
const parent = table.parentElement
|
|
148
|
-
if (parent?.dataset?.
|
|
161
|
+
if (parent?.dataset?.tableContainer === 'true') continue
|
|
149
162
|
const container = document.createElement('div')
|
|
150
163
|
container.setAttribute('class', classNames(styles.overflowContainerStyle))
|
|
151
|
-
container.dataset.
|
|
164
|
+
container.dataset.tableContainer = 'true'
|
|
152
165
|
table.replaceWith(container)
|
|
153
166
|
container.appendChild(table)
|
|
154
167
|
}
|
|
155
168
|
|
|
169
|
+
// Nested, so that the table of contents can show a heading under the one it
|
|
170
|
+
// belongs to. Cut to depth before nesting: filtering the tree afterwards
|
|
171
|
+
// would only reach the headings sitting at its root.
|
|
156
172
|
return {
|
|
157
|
-
headings: flat.filter((h) => h.depth <= tocMaxDepth)
|
|
173
|
+
headings: treefy(flat.filter((h) => h.depth <= tocMaxDepth))
|
|
158
174
|
}
|
|
159
175
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { classNames, cleanUrl } from '../html'
|
|
2
|
+
import type { DirectiveRenderer } from './context'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `width` and `height` are pixels. `max-width` goes with a width so that the
|
|
6
|
+
* image shrinks inside its column on a narrow screen rather than overflowing
|
|
7
|
+
* it; with a height alone there is nothing to overflow, since the width
|
|
8
|
+
* follows the proportions.
|
|
9
|
+
*/
|
|
10
|
+
const sizeStyle = (
|
|
11
|
+
width: string | undefined,
|
|
12
|
+
height: string | undefined
|
|
13
|
+
): string | undefined => {
|
|
14
|
+
const rules: string[] = []
|
|
15
|
+
if (width) rules.push(`width:${width}px`)
|
|
16
|
+
if (height) rules.push(`height:${height}px`)
|
|
17
|
+
if (width) rules.push('max-width:100%')
|
|
18
|
+
return rules.length > 0 ? rules.join(';') : undefined
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const imageRenderers: Record<string, DirectiveRenderer> = {
|
|
22
|
+
img: ({ attrs, root, nl }) =>
|
|
23
|
+
`<img${root({
|
|
24
|
+
class: classNames(attrs.class),
|
|
25
|
+
src: cleanUrl(attrs.src),
|
|
26
|
+
alt: attrs.alt ?? '',
|
|
27
|
+
style: sizeStyle(attrs.width, attrs.height)
|
|
28
|
+
})}>${nl}`
|
|
29
|
+
}
|
|
@@ -2,6 +2,7 @@ import { GRID_RE } from '../registry'
|
|
|
2
2
|
import { cardRenderers } from './card'
|
|
3
3
|
import type { DirectiveRenderer } from './context'
|
|
4
4
|
import { faqRenderers } from './faq'
|
|
5
|
+
import { imageRenderers } from './image'
|
|
5
6
|
import { cellRenderers, grid } from './grid'
|
|
6
7
|
import { linkButtonRenderers } from './linkButton'
|
|
7
8
|
import { youtubeRenderers } from './youtube'
|
|
@@ -10,6 +11,7 @@ const BY_NAME: Record<string, DirectiveRenderer> = {
|
|
|
10
11
|
...cardRenderers,
|
|
11
12
|
...cellRenderers,
|
|
12
13
|
...faqRenderers,
|
|
14
|
+
...imageRenderers,
|
|
13
15
|
...youtubeRenderers,
|
|
14
16
|
...linkButtonRenderers
|
|
15
17
|
}
|
|
@@ -40,10 +40,23 @@ export const cleanUrl = (href: string | undefined): string | undefined => {
|
|
|
40
40
|
|
|
41
41
|
export type AttrValue = string | boolean | undefined | null
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Attributes where an empty value says something, and so is written out. On an
|
|
45
|
+
* image an empty alt marks it as decoration; leaving the attribute off instead
|
|
46
|
+
* makes a screen reader read the file name.
|
|
47
|
+
*/
|
|
48
|
+
const KEEP_WHEN_EMPTY = new Set(['alt'])
|
|
49
|
+
|
|
43
50
|
/** `{a: 'x', b: true, c: undefined}` becomes ` a="x" b` */
|
|
44
51
|
export const attrsToHtml = (attrs: Record<string, AttrValue>): string =>
|
|
45
52
|
Object.entries(attrs)
|
|
46
|
-
.filter(
|
|
53
|
+
.filter(
|
|
54
|
+
([k, v]) =>
|
|
55
|
+
v !== undefined &&
|
|
56
|
+
v !== null &&
|
|
57
|
+
v !== false &&
|
|
58
|
+
(v !== '' || KEEP_WHEN_EMPTY.has(k))
|
|
59
|
+
)
|
|
47
60
|
.map(([k, v]) => (v === true ? ` ${k}` : ` ${k}="${escapeHtml(v)}"`))
|
|
48
61
|
.join('')
|
|
49
62
|
|
|
@@ -24,10 +24,10 @@ export function directiveRenderer(
|
|
|
24
24
|
const { name, kind, attrs, tokens } = token
|
|
25
25
|
const nl = kind === TEXT ? '' : '\n'
|
|
26
26
|
|
|
27
|
-
// Marks
|
|
28
|
-
//
|
|
27
|
+
// Marks the element as dressed, so that the pass which styles the rest of
|
|
28
|
+
// the document leaves it alone. The value names the directive it came from.
|
|
29
29
|
const own: DirectiveContext['own'] = (values) =>
|
|
30
|
-
attrsToHtml({ ...values, 'data-
|
|
30
|
+
attrsToHtml({ ...values, 'data-styled': name })
|
|
31
31
|
|
|
32
32
|
const context: DirectiveContext = {
|
|
33
33
|
token,
|
|
@@ -168,9 +168,16 @@ const purifier = (): DOMPurify => {
|
|
|
168
168
|
element.removeAttribute('style')
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
// A link that opens elsewhere must not hand the opener over with it.
|
|
171
|
+
// A link that opens elsewhere must not hand the opener over with it. What
|
|
172
|
+
// the document asked for is kept alongside rather than replaced.
|
|
172
173
|
if (element.getAttribute('target') === '_blank') {
|
|
173
|
-
element.
|
|
174
|
+
const asked = (element.getAttribute('rel') ?? '').split(/\s+/)
|
|
175
|
+
element.setAttribute(
|
|
176
|
+
'rel',
|
|
177
|
+
Array.from(
|
|
178
|
+
new Set(['noopener', 'noreferrer', ...asked.filter(Boolean)])
|
|
179
|
+
).join(' ')
|
|
180
|
+
)
|
|
174
181
|
}
|
|
175
182
|
})
|
|
176
183
|
return instance
|
|
@@ -101,6 +101,23 @@ describe('Markdown', () => {
|
|
|
101
101
|
expect(html).toMatchSnapshot()
|
|
102
102
|
})
|
|
103
103
|
|
|
104
|
+
// The nesting is read from the DOM rather than left to the snapshot, so a
|
|
105
|
+
// return to one flat list says which shape was lost.
|
|
106
|
+
it('should put a heading under the one it belongs to', async () => {
|
|
107
|
+
render(<Markdown showToc>{'# 章1\n\n## 節1\n\n## 節2\n\n# 章2'}</Markdown>)
|
|
108
|
+
const toc = await screen.findByRole('navigation')
|
|
109
|
+
|
|
110
|
+
const chapters = toc.querySelectorAll(':scope > ul > li')
|
|
111
|
+
expect(
|
|
112
|
+
Array.from(chapters, (li) => li.querySelector('a')?.textContent)
|
|
113
|
+
).toEqual(['章1', '章2'])
|
|
114
|
+
|
|
115
|
+
const sections = chapters[0].querySelectorAll(':scope > ul > li')
|
|
116
|
+
expect(
|
|
117
|
+
Array.from(sections, (li) => li.querySelector('a')?.textContent)
|
|
118
|
+
).toEqual(['節1', '節2'])
|
|
119
|
+
})
|
|
120
|
+
|
|
104
121
|
it('should use the given title', async () => {
|
|
105
122
|
render(
|
|
106
123
|
<Markdown showToc tocTitle="もくじ">
|
|
@@ -1,78 +1,78 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
3
|
exports[`Markdown > directives > should render card 1`] = `
|
|
4
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="border border-solid border-solid-gray-500 rounded-2xl sm:rounded-3xl text-solid-gray-900 overflow-hidden" data-
|
|
5
|
-
<h3 class="m-0 text-base leading-[2rem] font-medium first:pt-4 pt-2 last:pb-4 px-6" data-
|
|
6
|
-
<div class="text-base-sm leading-[1.85rem] first:pt-4 pt-2 last:pb-4 px-6" data-
|
|
4
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="border border-solid border-solid-gray-500 rounded-2xl sm:rounded-3xl text-solid-gray-900 overflow-hidden" data-styled="card"><img class="object-cover mb-2 w-full aspect-[352/226]" src="/a.png" alt="図" data-styled="card-img">
|
|
5
|
+
<h3 class="m-0 text-base leading-[2rem] font-medium first:pt-4 pt-2 last:pb-4 px-6" data-styled="card-title" id="タイトル">タイトル</h3>
|
|
6
|
+
<div class="text-base-sm leading-[1.85rem] first:pt-4 pt-2 last:pb-4 px-6" data-styled="card-description">説明</div>
|
|
7
7
|
</div>
|
|
8
8
|
</div></div>"
|
|
9
9
|
`;
|
|
10
10
|
|
|
11
11
|
exports[`Markdown > directives > should render cell 1`] = `
|
|
12
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div data-
|
|
12
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div data-styled="cell"><img class="mb-4" src="/a.png" alt="図" data-styled="cell-img">
|
|
13
13
|
<p>本文</p>
|
|
14
14
|
</div>
|
|
15
15
|
</div></div>"
|
|
16
16
|
`;
|
|
17
17
|
|
|
18
18
|
exports[`Markdown > directives > should render faq 1`] = `
|
|
19
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><dl class="flex flex-col gap-8" data-
|
|
20
|
-
<dd class="flex flex-row items-start gap-8" data-
|
|
21
|
-
<dt class="flex flex-row gap-8 mt-8 text-h-med-m sm:text-h-med" data-
|
|
22
|
-
<dd class="flex flex-row items-start gap-8" data-
|
|
19
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><dl class="flex flex-col gap-8" data-styled="faq"><dt class="flex flex-row gap-8 mt-8 text-h-med-m sm:text-h-med" data-styled="faq-q"><span aria-hidden="true">Q</span><span>質問は?</span></dt>
|
|
20
|
+
<dd class="flex flex-row items-start gap-8" data-styled="faq-a"><span class="text-h-med-m sm:text-h-med !leading-none" aria-hidden="true">A</span><span>回答です。</span></dd>
|
|
21
|
+
<dt class="flex flex-row gap-8 mt-8 text-h-med-m sm:text-h-med" data-styled="faq-q"><span aria-hidden="true">Q</span><span>二つ目の質問は?</span></dt>
|
|
22
|
+
<dd class="flex flex-row items-start gap-8" data-styled="faq-a"><span class="text-h-med-m sm:text-h-med !leading-none" aria-hidden="true">A</span><span>二つ目の回答。</span></dd>
|
|
23
23
|
</dl>
|
|
24
24
|
</div></div>"
|
|
25
25
|
`;
|
|
26
26
|
|
|
27
27
|
exports[`Markdown > directives > should render grid 1`] = `
|
|
28
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><ul class="flex flex-col md:grid md:grid-cols-3 gap-8" data-
|
|
28
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><ul class="flex flex-col md:grid md:grid-cols-3 gap-8" data-styled="grid-cols-3"><li class="sm:grid"><div class="border border-solid border-solid-gray-500 rounded-2xl sm:rounded-3xl text-solid-gray-900 overflow-hidden" data-styled="card"><h3 class="m-0 text-base leading-[2rem] font-medium first:pt-4 pt-2 last:pb-4 px-6" data-styled="card-title" id="1つ目">1つ目</h3>
|
|
29
29
|
</div></li>
|
|
30
|
-
<li class="sm:grid"><div class="border border-solid border-solid-gray-500 rounded-2xl sm:rounded-3xl text-solid-gray-900 overflow-hidden" data-
|
|
30
|
+
<li class="sm:grid"><div class="border border-solid border-solid-gray-500 rounded-2xl sm:rounded-3xl text-solid-gray-900 overflow-hidden" data-styled="card"><h3 class="m-0 text-base leading-[2rem] font-medium first:pt-4 pt-2 last:pb-4 px-6" data-styled="card-title" id="2つ目">2つ目</h3>
|
|
31
31
|
</div></li>
|
|
32
32
|
</ul>
|
|
33
33
|
</div></div>"
|
|
34
34
|
`;
|
|
35
35
|
|
|
36
36
|
exports[`Markdown > directives > should render gridMixed 1`] = `
|
|
37
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="flex flex-col md:grid md:grid-cols-2 gap-8" data-
|
|
37
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="flex flex-col md:grid md:grid-cols-2 gap-8" data-styled="grid-cols-2"><div class="border border-solid border-solid-gray-500 rounded-2xl sm:rounded-3xl text-solid-gray-900 overflow-hidden" data-styled="card"><h3 class="m-0 text-base leading-[2rem] font-medium first:pt-4 pt-2 last:pb-4 px-6" data-styled="card-title" id="1つ目">1つ目</h3>
|
|
38
38
|
</div>
|
|
39
|
-
<div data-
|
|
39
|
+
<div data-styled="cell"><p>本文</p>
|
|
40
40
|
</div>
|
|
41
41
|
</div>
|
|
42
42
|
</div></div>"
|
|
43
43
|
`;
|
|
44
44
|
|
|
45
45
|
exports[`Markdown > directives > should render gridOfCells 1`] = `
|
|
46
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="flex flex-col md:grid md:grid-cols-2 gap-8" data-
|
|
46
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="flex flex-col md:grid md:grid-cols-2 gap-8" data-styled="grid-cols-2"><div data-styled="cell"><p><img src="/a.png" alt="alt"><br>本文1</p>
|
|
47
47
|
</div>
|
|
48
|
-
<div data-
|
|
48
|
+
<div data-styled="cell"><p>本文2</p>
|
|
49
49
|
</div>
|
|
50
50
|
</div>
|
|
51
51
|
</div></div>"
|
|
52
52
|
`;
|
|
53
53
|
|
|
54
54
|
exports[`Markdown > directives > should render linkButton 1`] = `
|
|
55
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><p>ご案内です。<a class="inline-block text-button text-center cursor-pointer select-none touch-manipulation whitespace-nowrap outline-offset-2 border border-solid border-blue-900 hover:border-blue-1000 active:border-blue-1200 disabled:border-solid-gray-500 disabled:cursor-not-allowed text-blue-900 hover:text-blue-1000 active:text-blue-1200 bg-transparent hover:bg-blue-200 active:bg-blue-300 disabled:bg-transparent disabled:text-solid-gray-500 hover:underline hover:underline-offset-[calc(3/16*1rem)] focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:rounded-sm p-4 text-button leading-snug rounded-lg" href="/services" data-
|
|
55
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><p>ご案内です。<a class="inline-block text-button text-center cursor-pointer select-none touch-manipulation whitespace-nowrap outline-offset-2 border border-solid border-blue-900 hover:border-blue-1000 active:border-blue-1200 disabled:border-solid-gray-500 disabled:cursor-not-allowed text-blue-900 hover:text-blue-1000 active:text-blue-1200 bg-transparent hover:bg-blue-200 active:bg-blue-300 disabled:bg-transparent disabled:text-solid-gray-500 hover:underline hover:underline-offset-[calc(3/16*1rem)] focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:rounded-sm p-4 text-button leading-snug rounded-lg" href="/services" data-styled="link-button">サービス一覧</a> をどうぞ。</p>
|
|
56
56
|
</div></div>"
|
|
57
57
|
`;
|
|
58
58
|
|
|
59
59
|
exports[`Markdown > directives > should render linkCard 1`] = `
|
|
60
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="border border-solid border-solid-gray-500 rounded-2xl sm:rounded-3xl text-solid-gray-900 overflow-hidden relative z-0 group hover:outline hover:outline-2 hover:outline-black hover:bg-solid-gray-50 has-[a:focus-visible]:outline has-[a:focus-visible]:outline-4 has-[a:focus-visible]:outline-black has-[a:focus-visible]:outline-offset-[calc(2/16*1rem)] has-[a:focus-visible]:ring-[calc(2/16*1rem)] has-[a:focus-visible]:ring-yellow-300" data-
|
|
61
|
-
<div class="text-base-sm leading-[1.85rem] first:pt-4 pt-2 last:pb-4 px-6" data-
|
|
62
|
-
<div class="sticky top-full text-base-sm leading-[1.85rem] first:pt-4 pt-2 last:pb-4 px-6 flex justify-between items-center" data-
|
|
60
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="border border-solid border-solid-gray-500 rounded-2xl sm:rounded-3xl text-solid-gray-900 overflow-hidden relative z-0 group hover:outline hover:outline-2 hover:outline-black hover:bg-solid-gray-50 has-[a:focus-visible]:outline has-[a:focus-visible]:outline-4 has-[a:focus-visible]:outline-black has-[a:focus-visible]:outline-offset-[calc(2/16*1rem)] has-[a:focus-visible]:ring-[calc(2/16*1rem)] has-[a:focus-visible]:ring-yellow-300" data-styled="card"><h3 class="m-0 text-base leading-[2rem] font-medium first:pt-4 pt-2 last:pb-4 px-6 decoration-blue-900 underline underline-offset-[calc(3/16*1rem)] group-hover:text-blue-900 group-hover:decoration-[calc(3/16*1rem)]" data-styled="card-title" id="タイトル"><a class="after:content-[''] after:absolute after:inset-0 after:z-10 focus-visible:outline-none" href="/x" data-styled="card-title">タイトル</a></h3>
|
|
61
|
+
<div class="text-base-sm leading-[1.85rem] first:pt-4 pt-2 last:pb-4 px-6" data-styled="card-description">説明</div>
|
|
62
|
+
<div class="sticky top-full text-base-sm leading-[1.85rem] first:pt-4 pt-2 last:pb-4 px-6 flex justify-between items-center" data-styled="card-footer"><span>2026年8月</span><span class="inline-flex items-center justify-center w-6 h-6 text-blue-1000 border border-blue-1000 rounded-full group-hover:bg-blue-1000 group-hover:text-white"><span aria-hidden="true" class="!text-[16px] font-icon font-light inline-block leading-none align-middle whitespace-nowrap">arrow_forward</span><span class="sr-only"></span></span></div>
|
|
63
63
|
</div>
|
|
64
64
|
</div></div>"
|
|
65
65
|
`;
|
|
66
66
|
|
|
67
67
|
exports[`Markdown > directives > should render linkCardWithoutTitle 1`] = `
|
|
68
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="border border-solid border-solid-gray-500 rounded-2xl sm:rounded-3xl text-solid-gray-900 overflow-hidden" data-
|
|
69
|
-
<div class="sticky top-full text-base-sm leading-[1.85rem] first:pt-4 pt-2 last:pb-4 px-6" data-
|
|
68
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="border border-solid border-solid-gray-500 rounded-2xl sm:rounded-3xl text-solid-gray-900 overflow-hidden" data-styled="card"><div class="text-base-sm leading-[1.85rem] first:pt-4 pt-2 last:pb-4 px-6" data-styled="card-description">説明</div>
|
|
69
|
+
<div class="sticky top-full text-base-sm leading-[1.85rem] first:pt-4 pt-2 last:pb-4 px-6" data-styled="card-footer">2026年8月</div>
|
|
70
70
|
</div>
|
|
71
71
|
</div></div>"
|
|
72
72
|
`;
|
|
73
73
|
|
|
74
74
|
exports[`Markdown > directives > should render youtube 1`] = `
|
|
75
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><iframe title="動画タイトル" src="https://www.youtube-nocookie.com/embed/yXdbvBzxeb8" class="aspect-video w-full max-w-[470px]" style="width:560px;" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy" data-
|
|
75
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><iframe title="動画タイトル" src="https://www.youtube-nocookie.com/embed/yXdbvBzxeb8" class="aspect-video w-full max-w-[470px]" style="width:560px;" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy" data-styled="youtube"></iframe>
|
|
76
76
|
</div></div>"
|
|
77
77
|
`;
|
|
78
78
|
|
|
@@ -126,9 +126,9 @@ exports[`Markdown > malformed html > should recover from crossedNesting 1`] = `
|
|
|
126
126
|
</ul></div></div>"
|
|
127
127
|
`;
|
|
128
128
|
|
|
129
|
-
exports[`Markdown > malformed html > should recover from mistypedClose 1`] = `"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="w-full overflow-x-auto whitespace-nowrap print:overflow-visible print:whitespace-normal" data-
|
|
129
|
+
exports[`Markdown > malformed html > should recover from mistypedClose 1`] = `"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="w-full overflow-x-auto whitespace-nowrap print:overflow-visible print:whitespace-normal" data-table-container="true"><table class="border border-collapse border-solid-gray-420"><tbody><tr class="border border-collapse border-solid-gray-420"><td class="p-2 text-label border border-collapse border-solid-gray-420">値</td></tr></tbody></table></div></div></div>"`;
|
|
130
130
|
|
|
131
|
-
exports[`Markdown > malformed html > should recover from unclosedCell 1`] = `"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="w-full overflow-x-auto whitespace-nowrap print:overflow-visible print:whitespace-normal" data-
|
|
131
|
+
exports[`Markdown > malformed html > should recover from unclosedCell 1`] = `"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="w-full overflow-x-auto whitespace-nowrap print:overflow-visible print:whitespace-normal" data-table-container="true"><table class="border border-collapse border-solid-gray-420"><tbody><tr class="border border-collapse border-solid-gray-420"><td class="p-2 text-label border border-collapse border-solid-gray-420">閉じ忘れ</td></tr><tr class="border border-collapse border-solid-gray-420"><td class="p-2 text-label border border-collapse border-solid-gray-420">次の行</td></tr></tbody></table></div></div></div>"`;
|
|
132
132
|
|
|
133
133
|
exports[`Markdown > plain markdown > should render blockquote 1`] = `
|
|
134
134
|
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><blockquote>
|
|
@@ -156,7 +156,7 @@ exports[`Markdown > plain markdown > should render footnote 1`] = `
|
|
|
156
156
|
`;
|
|
157
157
|
|
|
158
158
|
exports[`Markdown > plain markdown > should render gfmTable 1`] = `
|
|
159
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="w-full overflow-x-auto whitespace-nowrap print:overflow-visible print:whitespace-normal" data-
|
|
159
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="w-full overflow-x-auto whitespace-nowrap print:overflow-visible print:whitespace-normal" data-table-container="true"><table class="border border-collapse border-solid-gray-420">
|
|
160
160
|
<thead>
|
|
161
161
|
<tr class="border border-collapse border-solid-gray-420">
|
|
162
162
|
<th class="p-2 bg-[#f8f8fb] text-label text-left border border-collapse border-solid-gray-420">見出し</th>
|
|
@@ -189,7 +189,7 @@ exports[`Markdown > plain markdown > should render image 1`] = `
|
|
|
189
189
|
`;
|
|
190
190
|
|
|
191
191
|
exports[`Markdown > plain markdown > should render links 1`] = `
|
|
192
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><p><a href="/foo" class="cursor-pointer text-blue-1000 active:text-orange-700 visited:text-magenta-900 focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:bg-yellow-300 focus-visible:rounded-sm underline underline-offset-[calc(3/16*1rem)] hover:decoration-[calc(3/16*1rem)] disabled:border-solid-gray-500 [overflow-wrap:anywhere]">内部</a> と <a href="https://example.com" target="_blank" rel="noopener noreferrer" class="cursor-pointer text-blue-1000 active:text-orange-700 visited:text-magenta-900 focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:bg-yellow-300 focus-visible:rounded-sm underline underline-offset-[calc(3/16*1rem)] hover:decoration-[calc(3/16*1rem)] disabled:border-solid-gray-500 [overflow-wrap:anywhere]">外部<span aria-hidden="true" data-
|
|
192
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><p><a href="/foo" class="cursor-pointer text-blue-1000 active:text-orange-700 visited:text-magenta-900 focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:bg-yellow-300 focus-visible:rounded-sm underline underline-offset-[calc(3/16*1rem)] hover:decoration-[calc(3/16*1rem)] disabled:border-solid-gray-500 [overflow-wrap:anywhere]">内部</a> と <a href="https://example.com" target="_blank" rel="noopener noreferrer" class="cursor-pointer text-blue-1000 active:text-orange-700 visited:text-magenta-900 focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:bg-yellow-300 focus-visible:rounded-sm underline underline-offset-[calc(3/16*1rem)] hover:decoration-[calc(3/16*1rem)] disabled:border-solid-gray-500 [overflow-wrap:anywhere]">外部<span aria-hidden="true" data-new-tab="true" class="!text-[16px] font-icon font-light inline-block leading-none align-middle whitespace-nowrap ml-0.5">open_in_new</span><span class="sr-only">Opens in new tab</span></a> と <a href="https://example.com/bare" target="_blank" rel="noopener noreferrer" class="cursor-pointer text-blue-1000 active:text-orange-700 visited:text-magenta-900 focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:bg-yellow-300 focus-visible:rounded-sm underline underline-offset-[calc(3/16*1rem)] hover:decoration-[calc(3/16*1rem)] disabled:border-solid-gray-500 [overflow-wrap:anywhere]">https://example.com/bare<span aria-hidden="true" data-new-tab="true" class="!text-[16px] font-icon font-light inline-block leading-none align-middle whitespace-nowrap ml-0.5">open_in_new</span><span class="sr-only">Opens in new tab</span></a></p>
|
|
193
193
|
</div></div>"
|
|
194
194
|
`;
|
|
195
195
|
|
|
@@ -226,7 +226,7 @@ exports[`Markdown > raw html > should render details 1`] = `
|
|
|
226
226
|
<summary>詳細はこちら</summary><ul class="list-disc [&_&]:list-circle [&_&_&]:list-square pl-8">
|
|
227
227
|
<li>リスト項目</li>
|
|
228
228
|
</ul>
|
|
229
|
-
<div class="w-full overflow-x-auto whitespace-nowrap print:overflow-visible print:whitespace-normal" data-
|
|
229
|
+
<div class="w-full overflow-x-auto whitespace-nowrap print:overflow-visible print:whitespace-normal" data-table-container="true"><table class="border border-collapse border-solid-gray-420">
|
|
230
230
|
<thead>
|
|
231
231
|
<tr class="border border-collapse border-solid-gray-420">
|
|
232
232
|
<th class="p-2 bg-[#f8f8fb] text-label text-left border border-collapse border-solid-gray-420">表</th>
|
|
@@ -242,7 +242,7 @@ exports[`Markdown > raw html > should render details 1`] = `
|
|
|
242
242
|
`;
|
|
243
243
|
|
|
244
244
|
exports[`Markdown > raw html > should render externalAnchor 1`] = `
|
|
245
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><p><a href="https://example.com" target="_blank" rel="noopener noreferrer" class="cursor-pointer text-blue-1000 active:text-orange-700 visited:text-magenta-900 focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:bg-yellow-300 focus-visible:rounded-sm underline underline-offset-[calc(3/16*1rem)] hover:decoration-[calc(3/16*1rem)] disabled:border-solid-gray-500 [overflow-wrap:anywhere]">外部リンク<span aria-hidden="true" data-
|
|
245
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><p><a href="https://example.com" target="_blank" rel="noopener noreferrer" class="cursor-pointer text-blue-1000 active:text-orange-700 visited:text-magenta-900 focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:bg-yellow-300 focus-visible:rounded-sm underline underline-offset-[calc(3/16*1rem)] hover:decoration-[calc(3/16*1rem)] disabled:border-solid-gray-500 [overflow-wrap:anywhere]">外部リンク<span aria-hidden="true" data-new-tab="true" class="!text-[16px] font-icon font-light inline-block leading-none align-middle whitespace-nowrap ml-0.5">open_in_new</span><span class="sr-only">Opens in new tab</span></a></p>
|
|
246
246
|
</div></div>"
|
|
247
247
|
`;
|
|
248
248
|
|
|
@@ -260,7 +260,7 @@ exports[`Markdown > raw html > should render styledBlock 1`] = `
|
|
|
260
260
|
exports[`Markdown > raw html > should render styledImg 1`] = `"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><img src="/images/a.png" alt="図" style="width: 800px; color-scheme: light;"></div></div>"`;
|
|
261
261
|
|
|
262
262
|
exports[`Markdown > raw html > should render tableSpan 1`] = `
|
|
263
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="w-full overflow-x-auto whitespace-nowrap print:overflow-visible print:whitespace-normal" data-
|
|
263
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><div class="w-full overflow-x-auto whitespace-nowrap print:overflow-visible print:whitespace-normal" data-table-container="true"><table class="border border-collapse border-solid-gray-420">
|
|
264
264
|
<caption class="text-left">表:01 キャプション</caption>
|
|
265
265
|
<colgroup><col style="width: 15%;"><col style="width: auto;"></colgroup>
|
|
266
266
|
<thead><tr class="border border-collapse border-solid-gray-420"><th colspan="2" class="p-2 bg-[#f8f8fb] text-label text-left border border-collapse border-solid-gray-420">見出し</th></tr></thead>
|
|
@@ -301,7 +301,12 @@ exports[`Markdown > table of contents > should render the table of contents when
|
|
|
301
301
|
|
|
302
302
|
disabled:border-solid-gray-500
|
|
303
303
|
[overflow-wrap:anywhere]
|
|
304
|
-
" href="#章1">章1</a
|
|
304
|
+
" href="#章1">章1</a><ul class="
|
|
305
|
+
list-disc
|
|
306
|
+
[&_&]:list-circle
|
|
307
|
+
[&_&_&]:list-square
|
|
308
|
+
pl-8
|
|
309
|
+
"><li><a class="
|
|
305
310
|
cursor-pointer
|
|
306
311
|
text-blue-1000
|
|
307
312
|
active:text-orange-700
|
|
@@ -355,7 +360,7 @@ exports[`Markdown > table of contents > should render the table of contents when
|
|
|
355
360
|
|
|
356
361
|
disabled:border-solid-gray-500
|
|
357
362
|
[overflow-wrap:anywhere]
|
|
358
|
-
" href="#節2">節2</a></li><li><a class="
|
|
363
|
+
" href="#節2">節2</a></li></ul></li><li><a class="
|
|
359
364
|
cursor-pointer
|
|
360
365
|
text-blue-1000
|
|
361
366
|
active:text-orange-700
|
|
@@ -411,7 +416,7 @@ exports[`Markdown > text that only looks like a directive > should leave functio
|
|
|
411
416
|
`;
|
|
412
417
|
|
|
413
418
|
exports[`Markdown > text that only looks like a directive > should leave linkLabel alone 1`] = `
|
|
414
|
-
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><p><a href="https://example.com" target="_blank" rel="noopener noreferrer" class="cursor-pointer text-blue-1000 active:text-orange-700 visited:text-magenta-900 focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:bg-yellow-300 focus-visible:rounded-sm underline underline-offset-[calc(3/16*1rem)] hover:decoration-[calc(3/16*1rem)] disabled:border-solid-gray-500 [overflow-wrap:anywhere]">AWSドキュメント:AWS PrivateLink<span aria-hidden="true" data-
|
|
419
|
+
"<div class="py-8 flex flex-col gap-8"><div class="flex flex-col gap-8"><p><a href="https://example.com" target="_blank" rel="noopener noreferrer" class="cursor-pointer text-blue-1000 active:text-orange-700 visited:text-magenta-900 focus-visible:outline-4 focus-visible:outline-black focus-visible:ring-yellow-300 focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:bg-yellow-300 focus-visible:rounded-sm underline underline-offset-[calc(3/16*1rem)] hover:decoration-[calc(3/16*1rem)] disabled:border-solid-gray-500 [overflow-wrap:anywhere]">AWSドキュメント:AWS PrivateLink<span aria-hidden="true" data-new-tab="true" class="!text-[16px] font-icon font-light inline-block leading-none align-middle whitespace-nowrap ml-0.5">open_in_new</span><span class="sr-only">Opens in new tab</span></a></p>
|
|
415
420
|
</div></div>"
|
|
416
421
|
`;
|
|
417
422
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { render } from '../src/render'
|
|
3
|
+
|
|
4
|
+
const html = (markdown: string) => render(markdown).html
|
|
5
|
+
const style = (markdown: string) => /style="([^"]*)"/.exec(html(markdown))?.[1]
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `::img` is the Markdown image plus a size. A document that needs one reaches
|
|
9
|
+
* for raw HTML and an inline style otherwise.
|
|
10
|
+
*/
|
|
11
|
+
describe('img', () => {
|
|
12
|
+
it('should render the source and the alternative text', () => {
|
|
13
|
+
const out = html('::img{src=/a.png alt="A field at dawn"}')
|
|
14
|
+
expect(out).toContain('src="/a.png"')
|
|
15
|
+
expect(out).toContain('alt="A field at dawn"')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('should take no size', () => {
|
|
19
|
+
expect(style('::img{src=/a.png alt=図}')).toBeUndefined()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should draw at the width asked for', () => {
|
|
23
|
+
expect(style('::img{src=/a.png alt=図 width=520}')).toBe(
|
|
24
|
+
'width:520px; max-width:100%;'
|
|
25
|
+
)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('should keep the image inside a narrow column', () => {
|
|
29
|
+
// Without max-width a fixed width overflows on a phone.
|
|
30
|
+
expect(style('::img{src=/a.png alt=図 width=1200}')).toContain(
|
|
31
|
+
'max-width:100%'
|
|
32
|
+
)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should draw at the height asked for', () => {
|
|
36
|
+
expect(style('::img{src=/a.png alt=図 height=300}')).toBe('height:300px;')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should take both', () => {
|
|
40
|
+
expect(style('::img{src=/a.png alt=図 width=520 height=300}')).toBe(
|
|
41
|
+
'width:520px; height:300px; max-width:100%;'
|
|
42
|
+
)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should survive the sanitiser', () => {
|
|
46
|
+
// width and max-width are on the CSS allowlist; a property that is not
|
|
47
|
+
// would be dropped here rather than in the renderer.
|
|
48
|
+
expect(html('::img{src=/a.png alt=図 width=520}')).toContain('520px')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should refuse a javascript URL', () => {
|
|
52
|
+
expect(html('::img{src=javascript:alert(1) alt=図}')).not.toContain(
|
|
53
|
+
'javascript:'
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should answer to {#name}', () => {
|
|
58
|
+
expect(html('::img{#figure-1 src=/a.png alt=図}')).toContain('id="figure-1"')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('should be empty alt when none is given', () => {
|
|
62
|
+
expect(html('::img{src=/a.png}')).toContain('alt=""')
|
|
63
|
+
})
|
|
64
|
+
})
|
|
@@ -135,6 +135,23 @@ describe('sanitize', () => {
|
|
|
135
135
|
expect(out).toContain('rel="noopener noreferrer"')
|
|
136
136
|
})
|
|
137
137
|
|
|
138
|
+
it('should keep a rel the document asked for as well', () => {
|
|
139
|
+
const out = html(
|
|
140
|
+
'<a href="https://example.com" target="_blank" rel="nofollow">外部</a>'
|
|
141
|
+
)
|
|
142
|
+
const rel = /rel="([^"]*)"/.exec(out)?.[1].split(' ')
|
|
143
|
+
expect(rel).toContain('noopener')
|
|
144
|
+
expect(rel).toContain('noreferrer')
|
|
145
|
+
expect(rel).toContain('nofollow')
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('should not repeat a rel the document already asked for', () => {
|
|
149
|
+
const out = html(
|
|
150
|
+
'<a href="https://example.com" target="_blank" rel="noopener">外部</a>'
|
|
151
|
+
)
|
|
152
|
+
expect(out).toContain('rel="noopener noreferrer"')
|
|
153
|
+
})
|
|
154
|
+
|
|
138
155
|
it('should say that the link opens elsewhere', () => {
|
|
139
156
|
const out = html('[外部](https://example.com)')
|
|
140
157
|
expect(out).toContain('Opens in new tab')
|