Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/content/3.rendering/3.vue.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ interface DefineComarkComponentOptions {
autoClose?: boolean
plugins?: ComarkPlugin[]
components?: Record<string, any>
/** Additional classes for the wrapper div */
class?: string
}
```

Expand Down Expand Up @@ -477,6 +479,8 @@ interface DefineComarkRendererOptions {
extends?: ReturnType<typeof defineComarkRendererComponent>
name?: string
components?: Record<string, any>
/** Additional classes for the wrapper div */
class?: string
}
```

Expand Down
4 changes: 4 additions & 0 deletions docs/content/3.rendering/4.react.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,8 @@ interface DefineComarkComponentOptions {
autoClose?: boolean
plugins?: ComarkPlugin[]
components?: Record<string, React.ComponentType<any>>
/** Additional classes for the wrapper div */
className?: string
}
```

Expand Down Expand Up @@ -489,6 +491,8 @@ interface DefineComarkRendererOptions {
extends?: React.FC<ComarkRendererProps>
name?: string
components?: Record<string, React.ComponentType<any>>
/** Additional classes for the wrapper div */
className?: string
}
```

Expand Down
18 changes: 16 additions & 2 deletions packages/comark-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ interface DefineComarkComponentOptions extends ParseOptions {
/** Display name shown in React DevTools. */
name?: string
components?: Record<string, React.ComponentType<any>>
/**
* Additional classes for the wrapper div
*/
className?: string
}

/**
Expand All @@ -42,7 +46,7 @@ interface DefineComarkComponentOptions extends ParseOptions {
* ```
*/
export function defineComarkComponent(config: DefineComarkComponentOptions = {}) {
const { name, components: configComponents = {}, extends: BaseComponent, ...parseOptions } = config
const { name, components: configComponents = {}, className: configClassName, extends: BaseComponent, ...parseOptions } = config

const ComarkComponent: React.FC<ComarkProps> = (props) => {
const mergedOptions: Exclude<ParseOptions, 'plugins'> = {
Expand All @@ -60,11 +64,14 @@ export function defineComarkComponent(config: DefineComarkComponentOptions = {})
...props.components,
}

const mergedClassName = [configClassName, props.className].filter(Boolean).join(' ') || undefined

return React.createElement(BaseComponent ?? Comark, {
...props,
options: mergedOptions,
plugins: mergedPlugins,
components: mergedComponents,
className: mergedClassName,
})
}

Expand All @@ -79,6 +86,10 @@ interface DefineComarkRendererOptions {
/** Display name shown in React DevTools. */
name?: string
components?: Record<string, React.ComponentType<any>>
/**
* Additional classes for the wrapper div
*/
className?: string
}

/**
Expand Down Expand Up @@ -108,17 +119,20 @@ interface DefineComarkRendererOptions {
* ```
*/
export function defineComarkRendererComponent(config: DefineComarkRendererOptions = {}) {
const { name, components: configComponents = {}, extends: BaseComponent } = config
const { name, components: configComponents = {}, className: configClassName, extends: BaseComponent } = config

const RendererComponent: React.FC<ComarkRendererProps> = (props) => {
const mergedComponents = {
...configComponents,
...props.components,
}

const mergedClassName = [configClassName, props.className].filter(Boolean).join(' ') || undefined

return React.createElement(BaseComponent ?? ComarkRenderer, {
...props,
components: mergedComponents,
className: mergedClassName,
})
}

Expand Down
67 changes: 67 additions & 0 deletions packages/comark-react/test/define-component.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,37 @@ describe('defineComarkComponent — plugin inheritance via extends', () => {
})
})

// ---------------------------------------------------------------------------
// defineComarkComponent — className
// ---------------------------------------------------------------------------

describe('defineComarkComponent — className via config', () => {
it('applies config className to wrapper div', async () => {
const Custom = defineComarkComponent({ name: 'WithClass', className: 'prose dark' })
const html = await renderAsync(<Custom markdown="hello" />)
expect(html).toContain('comark-content prose dark')
})

it('merges config className with prop className', async () => {
const Custom = defineComarkComponent({ name: 'WithClass', className: 'prose' })
const html = await renderAsync(<Custom markdown="hello" className="extra" />)
expect(html).toContain('prose extra')
})

it('prop className works without config className', async () => {
const Custom = defineComarkComponent({ name: 'NoConfigClass' })
const html = await renderAsync(<Custom markdown="hello" className="only-prop" />)
expect(html).toContain('comark-content only-prop')
})

it('inherited component preserves parent className', async () => {
const Base = defineComarkComponent({ name: 'Base', className: 'base-class' })
const Child = defineComarkComponent({ name: 'Child', extends: Base })
const html = await renderAsync(<Child markdown="hello" />)
expect(html).toContain('base-class')
})
})

// ---------------------------------------------------------------------------
// defineComarkRendererComponent
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -190,3 +221,39 @@ describe('defineComarkRendererComponent — component inheritance via extends',
expect(html).toContain('card-base')
})
})

// ---------------------------------------------------------------------------
// defineComarkRendererComponent — className
// ---------------------------------------------------------------------------

describe('defineComarkRendererComponent — className via config', () => {
it('applies config className to wrapper div', async () => {
const Renderer = defineComarkRendererComponent({ name: 'WithClass', className: 'prose dark' })
const tree = await parse('hello')
const html = await renderAsync(<Renderer tree={tree} />)
expect(html).toContain('comark-content prose dark')
})

it('merges config className with prop className', async () => {
const Renderer = defineComarkRendererComponent({ name: 'WithClass', className: 'prose' })
const tree = await parse('hello')
const html = await renderAsync(<Renderer tree={tree} className="extra" />)
expect(html).toContain('prose extra')
})

it('prop className works without config className', async () => {
const Renderer = defineComarkRendererComponent({ name: 'NoConfigClass' })
const tree = await parse('hello')
const html = await renderAsync(<Renderer tree={tree} className="only-prop" />)
expect(html).toContain('comark-content only-prop')
})

it('inherited renderer preserves parent className', async () => {
const Base = defineComarkRendererComponent({ name: 'BaseRenderer', className: 'base-class' })
const Child = defineComarkRendererComponent({ name: 'ChildRenderer', extends: Base, className: 'child-class' })
const tree = await parse('hello')
const html = await renderAsync(<Child tree={tree} />)
expect(html).toContain('base-class')
expect(html).toContain('child-class')
})
})
10 changes: 10 additions & 0 deletions packages/comark-vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@ interface DefineComarkComponentOptions extends ParseOptions {
extends?: typeof Comark
name?: string
components?: Record<string, any>
/**
* Additional classes for the wrapper div
*/
class?: string
}

interface DefineComarkRendererOptions {
extends?: typeof ComarkRenderer
name?: string
components?: Record<string, any>
/**
* Additional classes for the wrapper div
*/
class?: string
}

export function defineComarkComponent(config: DefineComarkComponentOptions = {}): typeof Comark {
Expand Down Expand Up @@ -120,6 +128,7 @@ export function defineComarkComponent(config: DefineComarkComponentOptions = {})
streaming: props.streaming,
summary: props.summary,
caret: props.caret,
class: config.class,
}, {
default: slots.default,
})
Expand Down Expand Up @@ -189,6 +198,7 @@ export function defineComarkRendererComponent(config: DefineComarkRendererOption
componentsManifest: props.componentsManifest,
streaming: props.streaming,
caret: props.caret,
class: config.class,
}, {
default: slots.default,
})
Expand Down
57 changes: 57 additions & 0 deletions packages/comark-vue/test/define-component.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,33 @@ describe('defineComarkComponent — plugin inheritance via extends', () => {
})
})

// ---------------------------------------------------------------------------
// defineComarkComponent — class
// ---------------------------------------------------------------------------

describe('defineComarkComponent — class via config', () => {
it('applies config class to wrapper div', async () => {
const Custom = defineComarkComponent({ name: 'WithClass', class: 'prose dark' })
const html = await renderComponent(Custom, { markdown: 'hello' })
expect(html).toContain('prose dark')
expect(html).toContain('comark-content')
})

it('prop class works without config class', async () => {
const Custom = defineComarkComponent({ name: 'NoConfigClass' })
const html = await renderComponent(Custom, { markdown: 'hello' })
expect(html).toContain('comark-content')
})

it('inherited component preserves parent class', async () => {
const Base = defineComarkComponent({ name: 'Base', class: 'base-class' })
const Child = defineComarkComponent({ name: 'Child', extends: Base, class: 'child-class' })
const html = await renderComponent(Child, { markdown: 'hello' })
expect(html).toContain('base-class')
expect(html).toContain('child-class')
})
})

// ---------------------------------------------------------------------------
// defineComarkRendererComponent
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -201,3 +228,33 @@ describe('defineComarkRendererComponent — component inheritance via extends',
expect(html).toContain('card-base')
})
})

// ---------------------------------------------------------------------------
// defineComarkRendererComponent — class
// ---------------------------------------------------------------------------

describe('defineComarkRendererComponent — class via config', () => {
it('applies config class to wrapper div', async () => {
const Renderer = defineComarkRendererComponent({ name: 'WithClass', class: 'prose dark' })
const tree = await parse('hello')
const html = await renderComponent(Renderer, { tree })
expect(html).toContain('prose dark')
expect(html).toContain('comark-content')
})

it('prop class works without config class', async () => {
const Renderer = defineComarkRendererComponent({ name: 'NoConfigClass' })
const tree = await parse('hello')
const html = await renderComponent(Renderer, { tree })
expect(html).toContain('comark-content')
})

it('inherited renderer preserves parent class', async () => {
const Base = defineComarkRendererComponent({ name: 'BaseRenderer', class: 'base-class' })
const Child = defineComarkRendererComponent({ name: 'ChildRenderer', extends: Base, class: 'child-class' })
const tree = await parse('hello')
const html = await renderComponent(Child, { tree })
expect(html).toContain('base-class')
expect(html).toContain('child-class')
})
})
Loading