Skip to content

Commit 16fed54

Browse files
authored
Merge pull request #593 from webstackdev/feature/article-final-touches
Feature/article final touches
2 parents f7f1801 + dafc77c commit 16fed54

25 files changed

Lines changed: 1572 additions & 479 deletions

File tree

‎_TODO.md‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ https://mermaid.js.org/config/directives.html
276276

277277
- How can we handle footnotes in List components? src/content/articles/kubernetes-multi-cluster-fleet-management-configuration/pdf.mdx line 80
278278

279+
- Need to update Case Studies with lists and tables too
280+
279281
### This file was badly mangled during refactoring, need to compare against original:
280282

281283
src/content/articles/internal-developer-portal-platform-self-service-actions/pdf.mdx
@@ -297,15 +299,45 @@ src/content/articles/internal-developer-portal-platform-self-service-actions/pdf
297299
[&>span]:
298300
[&>*:first-child]:
299301

302+
<List
303+
variant="numbered-with-background-list"
304+
classes={{
305+
li: "[&>div]:!pt-2 mb-6",
306+
}}
307+
/>
308+
300309
<List
301310
classes={{
302311
wrapper: "sm:ml-6 sm:mr-12 mb-6",
303312
}}
304313
/>
305314

315+
<List
316+
variant="colored-marker-list"
317+
items={[
318+
{
319+
text: "With trusted service providers who assist in operating our website",
320+
color: "bg-blue-600",
321+
},
322+
]}
323+
/>
324+
325+
### Spacing out the boxes:
326+
327+
<List
328+
variant="zebra-list"
329+
classes={{
330+
ul: "!divide-y-0 !border-0 !overflow-visible !rounded-none space-y-4",
331+
li: "!rounded-lg !border border-trim",
332+
}}
333+
/>
334+
306335
<Table
307336
fullWidth={false}
308337
classes={{
309338
figure: "sm:ml-8 sm:mr-12 mb-6",
310339
}}
340+
classes={{
341+
tbody: '!text-content',
342+
}}
311343
/>
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import { beforeEach, describe, expect, test } from 'vitest'
2+
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
3+
import FileExplorerAstro from '@components/FileExplorer/index.astro'
4+
import type { FileExplorerComponent } from '../index'
5+
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
6+
import { executeRender } from '@test/unit/helpers/litRuntime'
7+
8+
const pressKey = (element: HTMLElement, key: string): void => {
9+
const keyboardEvent = new element.ownerDocument.defaultView!.KeyboardEvent('keydown', {
10+
key,
11+
bubbles: true,
12+
})
13+
14+
element.dispatchEvent(keyboardEvent)
15+
}
16+
17+
type FileExplorerModule = WebComponentModule<FileExplorerComponent>
18+
19+
describe('FileExplorer class behavior', () => {
20+
let container: AstroContainer
21+
22+
beforeEach(async () => {
23+
container = await AstroContainer.create()
24+
})
25+
26+
const mockData = [
27+
{
28+
type: 'folder',
29+
name: 'src',
30+
note: 'Application source files and test fixtures.',
31+
children: [
32+
{ type: 'file', name: 'index.ts', comment: 'Main entry', note: 'Primary FileExplorer implementation.' },
33+
],
34+
},
35+
{ type: 'file', name: 'package.json' },
36+
]
37+
38+
const renderArgs = {
39+
props: {
40+
data: mockData,
41+
figure: 'Figure <strong>caption</strong>',
42+
},
43+
}
44+
45+
const runComponentRender = async (
46+
assertion: (_context: { element: FileExplorerComponent }) => Promise<void> | void
47+
): Promise<void> => {
48+
await executeRender<FileExplorerModule>({
49+
container,
50+
component: FileExplorerAstro,
51+
moduleSpecifier: '@components/FileExplorer/client/index',
52+
args: renderArgs,
53+
waitForReady: async (element: FileExplorerComponent) => {
54+
await element.updateComplete
55+
},
56+
assert: async ({
57+
element,
58+
module,
59+
renderResult,
60+
}: {
61+
element: FileExplorerComponent
62+
module: FileExplorerModule
63+
renderResult: string
64+
}) => {
65+
expect(renderResult).toContain(`<${module.registeredName}`)
66+
await assertion({ element })
67+
},
68+
})
69+
}
70+
71+
test('renders hierarchical file systems based on data prop', async () => {
72+
await runComponentRender(async ({ element }) => {
73+
const root = element
74+
75+
const nodes = root.querySelectorAll('.node-name')
76+
expect(nodes.length).toBe(3)
77+
78+
const folderNames = Array.from(nodes).map(n => n.textContent?.trim())
79+
expect(folderNames).toContain('src')
80+
expect(folderNames).toContain('index.ts')
81+
expect(folderNames).toContain('package.json')
82+
83+
const comments = root.querySelectorAll('.comment')
84+
expect(comments.length).toBe(1)
85+
const firstComment = comments.item(0)
86+
expect(firstComment).toBeTruthy()
87+
expect(firstComment?.textContent?.trim()).toBe('# Main entry')
88+
89+
const tooltips = root.querySelectorAll('[role="tooltip"]')
90+
expect(tooltips.length).toBe(2)
91+
const tooltipTexts = Array.from(tooltips).map((tooltip) => tooltip.textContent?.trim())
92+
expect(tooltipTexts).toContain('Application source files and test fixtures.')
93+
expect(tooltipTexts).toContain('Primary FileExplorer implementation.')
94+
95+
const figure = root.querySelector('figure')
96+
expect(figure).toBeTruthy()
97+
98+
const figcaption = root.querySelector('figcaption')
99+
expect(figcaption).toBeTruthy()
100+
expect(figcaption?.textContent?.trim()).toBe('Figure caption')
101+
expect(figcaption?.querySelector('strong')?.textContent).toBe('caption')
102+
103+
const tree = root.querySelector('[role="tree"]')
104+
expect(tree).toBeTruthy()
105+
106+
const folderTreeItem = root.querySelector<HTMLElement>('[data-treeitem-path="0"]')
107+
expect(folderTreeItem?.getAttribute('role')).toBe('treeitem')
108+
expect(folderTreeItem?.getAttribute('aria-controls')).toBeTruthy()
109+
expect(folderTreeItem?.getAttribute('aria-level')).toBe('1')
110+
111+
const childGroup = root.querySelector('[role="group"]')
112+
expect(childGroup).toBeTruthy()
113+
114+
const decorativeIcons = root.querySelectorAll('svg[aria-hidden="true"]')
115+
expect(decorativeIcons.length).toBeGreaterThan(0)
116+
})
117+
})
118+
119+
test('collapses and expands directories when the folder row is clicked', async () => {
120+
await runComponentRender(async ({ element }) => {
121+
const toggle = element.querySelector<HTMLButtonElement>('button[data-node-path="0"]')
122+
const indicator = toggle?.querySelector('[data-toggle-indicator]')
123+
124+
expect(toggle).toBeTruthy()
125+
expect(indicator).toBeTruthy()
126+
expect(toggle?.getAttribute('aria-expanded')).toBe('true')
127+
expect(element.textContent).toContain('index.ts')
128+
129+
toggle?.click()
130+
await element.updateComplete
131+
132+
expect(toggle?.getAttribute('aria-expanded')).toBe('false')
133+
expect(element.textContent).not.toContain('index.ts')
134+
135+
toggle?.click()
136+
await element.updateComplete
137+
138+
expect(toggle?.getAttribute('aria-expanded')).toBe('true')
139+
expect(element.textContent).toContain('index.ts')
140+
})
141+
})
142+
143+
test('supports tree keyboard navigation and roving tabindex', async () => {
144+
await runComponentRender(async ({ element }) => {
145+
const rootTreeItem = element.querySelector<HTMLElement>('[data-treeitem-path="0"]')
146+
const childTreeItem = element.querySelector<HTMLElement>('[data-treeitem-path="0/0"]')
147+
148+
expect(rootTreeItem).toBeTruthy()
149+
expect(childTreeItem).toBeTruthy()
150+
expect(rootTreeItem?.getAttribute('tabindex')).toBe('0')
151+
expect(childTreeItem?.getAttribute('tabindex')).toBe('-1')
152+
153+
rootTreeItem?.focus()
154+
pressKey(rootTreeItem!, 'ArrowDown')
155+
await element.updateComplete
156+
157+
expect(childTreeItem?.getAttribute('tabindex')).toBe('0')
158+
159+
pressKey(rootTreeItem!, 'ArrowLeft')
160+
await element.updateComplete
161+
expect(rootTreeItem?.getAttribute('aria-expanded')).toBe('false')
162+
163+
pressKey(rootTreeItem!, 'ArrowRight')
164+
await element.updateComplete
165+
expect(rootTreeItem?.getAttribute('aria-expanded')).toBe('true')
166+
})
167+
})
168+
})

0 commit comments

Comments
 (0)