Skip to content

Commit a4c8bde

Browse files
committed
feat: simple markdown component
Signed-off-by: Philippe Martin <[email protected]>
1 parent 7ee43ee commit a4c8bde

File tree

5 files changed

+381
-3
lines changed

5 files changed

+381
-3
lines changed

packages/webview/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"@xterm/xterm": "^5.5.0",
5050
"humanize-duration": "^3.33.0",
5151
"jsdom": "^27.0.0",
52+
"micromark": "^4.0.2",
5253
"monaco-editor": "^0.54.0",
5354
"prettier": "^3.6.1",
5455
"prettier-plugin-svelte": "^3.3.3",
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<script lang="ts">
2+
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons';
3+
import { API_NAVIGATION, type KubernetesProvider } from '@kubernetes-dashboard/channels';
4+
import { Button } from '@podman-desktop/ui-svelte';
5+
import Fa from 'svelte-fa';
6+
import IconImage from '/@/component/icons/IconImage.svelte';
7+
import { getContext } from 'svelte';
8+
import { Remote } from '/@/remote/remote';
9+
import Markdown from '/@/markdown/Markdown.svelte';
10+
11+
interface Props {
12+
provider: KubernetesProvider;
13+
}
14+
15+
let { provider }: Props = $props();
16+
17+
const remote = getContext<Remote>(Remote);
18+
const navigationApi = remote.getProxy(API_NAVIGATION);
19+
20+
async function createNew(provider: KubernetesProvider): Promise<void> {
21+
return navigationApi.navigateToProviderNewConnection(provider.id);
22+
}
23+
</script>
24+
25+
<div class="rounded-xl p-5 text-left bg-[var(--pd-content-card-bg)]">
26+
<div class="flex justify-left text-[var(--pd-details-empty-icon)] py-2 mb-2">
27+
<IconImage image={provider?.images?.icon} class="mx-0 max-h-10" alt={provider.creationDisplayName}></IconImage>
28+
</div>
29+
<h1 class="text-lg font-semibold mb-4">
30+
{provider.creationDisplayName ?? 'Create'}
31+
</h1>
32+
33+
<p class="text-sm text-[var(--pd-content-text)] mb-6">
34+
<Markdown markdown={provider.emptyConnectionMarkdownDescription} />
35+
</p>
36+
37+
<div class="flex justify-center">
38+
<Button
39+
type="primary"
40+
on:click={(): Promise<void> => createNew(provider)}
41+
class="flex items-center"
42+
aria-label={provider.creationButtonTitle ?? 'Create new'}>
43+
<Fa icon={faPlusCircle} size="1.2x" class="mr-1" />
44+
{provider.creationButtonTitle ?? 'Create new'}
45+
</Button>
46+
</div>
47+
</div>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**********************************************************************
2+
* Copyright (C) 2023 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
***********************************************************************/
18+
19+
import '@testing-library/jest-dom/vitest';
20+
21+
import { render, screen } from '@testing-library/svelte';
22+
import { expect, test } from 'vitest';
23+
24+
import Markdown from './Markdown.svelte';
25+
26+
test('Expect to have bold', async () => {
27+
const component = render(Markdown, { markdown: '**bold**' });
28+
const markdownContent = screen.getByRole('region', { name: 'markdown-content' });
29+
expect(markdownContent).toBeInTheDocument();
30+
expect(markdownContent).toContainHTML('<strong>bold</strong>');
31+
32+
await component.rerender({ markdown: '**bold2**' });
33+
expect(markdownContent).toContainHTML('<strong>bold2</strong>');
34+
});
35+
36+
test('Expect to have italic', async () => {
37+
const component = render(Markdown, { markdown: '_italic_' });
38+
const markdownContent = screen.getByRole('region', { name: 'markdown-content' });
39+
expect(markdownContent).toBeInTheDocument();
40+
expect(markdownContent).toContainHTML('<em>italic</em>');
41+
42+
await component.rerender({ markdown: '_italic2_' });
43+
expect(markdownContent).toContainHTML('<em>italic2</em>');
44+
});
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<!-- The markdown rendered has it's own style that you'll have to customize / check against podman desktop
2+
UI guidelines -->
3+
<style lang="postcss">
4+
.markdown > :global(p) {
5+
line-height: normal;
6+
padding-bottom: 8px;
7+
margin-bottom: 8px;
8+
}
9+
10+
.markdown > :global(h1),
11+
:global(h2),
12+
:global(h3),
13+
:global(h4),
14+
:global(h5) {
15+
font-size: revert;
16+
line-height: normal;
17+
font-weight: revert;
18+
border-bottom: 1px solid #444;
19+
margin-bottom: 20px;
20+
}
21+
22+
.markdown > :global(ul) {
23+
line-height: normal;
24+
list-style: revert;
25+
margin: revert;
26+
padding: revert;
27+
}
28+
29+
.markdown > :global(b),
30+
:global(strong) {
31+
font-weight: 600;
32+
}
33+
.markdown > :global(blockquote) {
34+
opacity: 0.8;
35+
line-height: normal;
36+
}
37+
.markdown :global(a) {
38+
color: var(--pd-link);
39+
text-decoration: none;
40+
border-radius: 4px;
41+
}
42+
.markdown :global(a):hover {
43+
background-color: var(--pd-link-hover-bg);
44+
}
45+
</style>
46+
47+
<script lang="ts">
48+
import { micromark } from 'micromark';
49+
import { onMount } from 'svelte';
50+
51+
let text: string;
52+
let html: string;
53+
54+
// Optional attribute to specify the markdown to use
55+
// the user can use: <Markdown>**bold</Markdown> or <Markdown markdown="**bold**" /> syntax
56+
export let markdown = '';
57+
58+
// Render the markdown or the html+micromark markdown reactively
59+
$: markdown
60+
? // eslint-disable-next-line sonarjs/no-nested-assignment
61+
(html = micromark(markdown))
62+
: undefined;
63+
64+
onMount(() => {
65+
if (markdown) {
66+
text = markdown;
67+
}
68+
html = micromark(text);
69+
});
70+
</script>
71+
72+
<!-- Placeholder to grab the content if people are using <Markdown>**bold</Markdown> -->
73+
<span contenteditable="false" bind:textContent={text} class="hidden">
74+
<slot />
75+
</span>
76+
77+
<section class="markdown" aria-label="markdown-content">
78+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
79+
{@html html}
80+
</section>

0 commit comments

Comments
 (0)