Skip to content

Commit eff402e

Browse files
authored
add service pages + coverage data (#460)
1 parent 808bc4f commit eff402e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+21762
-206
lines changed
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
import React from 'react';
2+
const jsonData = import.meta.glob('/src/data/azure-coverage/*.json');
3+
import {
4+
Table,
5+
TableHeader,
6+
TableBody,
7+
TableRow,
8+
TableHead,
9+
TableCell,
10+
} from '@/components/ui/table';
11+
import {
12+
useReactTable,
13+
getCoreRowModel,
14+
getSortedRowModel,
15+
flexRender,
16+
getFilteredRowModel,
17+
getPaginationRowModel,
18+
} from '@tanstack/react-table';
19+
import type {
20+
SortingState,
21+
ColumnDef,
22+
ColumnFiltersState,
23+
} from '@tanstack/react-table';
24+
25+
type CoverageValue = {
26+
implemented?: boolean;
27+
};
28+
29+
type CoverageRow = Record<string, CoverageValue>;
30+
31+
type AzureCoverageFile = {
32+
service: string;
33+
operations?: CoverageRow[];
34+
details?: Record<string, Record<string, CoverageValue>>;
35+
};
36+
37+
const columns: ColumnDef<CoverageRow>[] = [
38+
{
39+
id: 'operation',
40+
accessorFn: (row) => Object.keys(row)[0],
41+
header: () => 'Operation',
42+
enableColumnFilter: true,
43+
filterFn: (row, _, filterValue) => {
44+
const operation = Object.keys(row.original)[0];
45+
return operation
46+
.toLowerCase()
47+
.includes((filterValue ?? '').toLowerCase());
48+
},
49+
enableResizing: false,
50+
},
51+
{
52+
id: 'implemented',
53+
accessorFn: (row) => row[Object.keys(row)[0]]?.implemented,
54+
header: () => 'Implemented',
55+
cell: ({ getValue }) => (getValue() ? '✔️' : ''),
56+
enableSorting: true,
57+
enableResizing: false,
58+
},
59+
];
60+
61+
function flattenDetails(
62+
details: Record<string, Record<string, CoverageValue>> = {}
63+
): CoverageRow[] {
64+
const rows: CoverageRow[] = [];
65+
66+
for (const groupName of Object.keys(details).sort()) {
67+
const group = details[groupName] ?? {};
68+
for (const operationName of Object.keys(group).sort()) {
69+
rows.push({
70+
[`${groupName}.${operationName}`]: group[operationName],
71+
});
72+
}
73+
}
74+
75+
return rows;
76+
}
77+
78+
export default function AzureFeatureCoverage({ service }: { service: string }) {
79+
const [coverage, setCoverage] = React.useState<CoverageRow[]>([]);
80+
const [sorting, setSorting] = React.useState<SortingState>([
81+
{ id: 'implemented', desc: true },
82+
{ id: 'operation', desc: false },
83+
]);
84+
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
85+
[]
86+
);
87+
88+
React.useEffect(() => {
89+
const loadData = async () => {
90+
const loader = jsonData[`/src/data/azure-coverage/${service}.json`];
91+
if (!loader) {
92+
setCoverage([]);
93+
return;
94+
}
95+
96+
const moduleData = (await loader()) as { default: AzureCoverageFile };
97+
const operations = moduleData.default.operations ?? [];
98+
const rows =
99+
operations.length > 0
100+
? operations
101+
: flattenDetails(moduleData.default.details);
102+
setCoverage(rows);
103+
};
104+
loadData();
105+
}, [service]);
106+
107+
const table = useReactTable({
108+
data: coverage,
109+
columns,
110+
state: {
111+
sorting,
112+
columnFilters,
113+
},
114+
onSortingChange: setSorting,
115+
onColumnFiltersChange: setColumnFilters,
116+
getCoreRowModel: getCoreRowModel(),
117+
getSortedRowModel: getSortedRowModel(),
118+
getFilteredRowModel: getFilteredRowModel(),
119+
getPaginationRowModel: getPaginationRowModel(),
120+
debugTable: false,
121+
initialState: { pagination: { pageSize: 12 } },
122+
});
123+
124+
return (
125+
<div className="w-full">
126+
<div style={{ marginBottom: 12, marginTop: 12 }}>
127+
<input
128+
type="text"
129+
placeholder="Filter by operation name..."
130+
value={
131+
(table.getColumn('operation')?.getFilterValue() as string) || ''
132+
}
133+
onChange={(e) =>
134+
table.getColumn('operation')?.setFilterValue(e.target.value)
135+
}
136+
className="border rounded px-2 py-1 w-full max-w-xs"
137+
style={{
138+
color: '#707385',
139+
fontFamily: 'AeonikFono',
140+
fontSize: '14px',
141+
fontStyle: 'normal',
142+
fontWeight: '500',
143+
lineHeight: '24px',
144+
letterSpacing: '-0.2px',
145+
}}
146+
/>
147+
</div>
148+
<div className="w-full overflow-x-auto">
149+
<div className="min-w-full">
150+
<Table
151+
className="w-full"
152+
style={{
153+
borderCollapse: 'collapse',
154+
tableLayout: 'fixed',
155+
width: '100%',
156+
}}
157+
>
158+
<TableHeader>
159+
{table.getHeaderGroups().map((headerGroup) => (
160+
<TableRow key={headerGroup.id}>
161+
{headerGroup.headers.map((header) => {
162+
const canSort = header.column.getCanSort();
163+
164+
const getColumnWidth = (columnId: string) => {
165+
switch (columnId) {
166+
case 'operation':
167+
return '85%';
168+
case 'implemented':
169+
return '15%';
170+
default:
171+
return '15%';
172+
}
173+
};
174+
175+
return (
176+
<TableHead
177+
key={header.id}
178+
onClick={
179+
canSort
180+
? header.column.getToggleSortingHandler()
181+
: undefined
182+
}
183+
className={canSort ? 'cursor-pointer select-none' : ''}
184+
style={{
185+
width: getColumnWidth(header.id),
186+
textAlign: header.id === 'operation' ? 'left' : 'center',
187+
border: '1px solid #999CAD',
188+
background: '#AFB2C2',
189+
color: 'var(--sl-color-gray-1)',
190+
fontFamily: 'AeonikFono',
191+
fontSize: '14px',
192+
fontStyle: 'normal',
193+
fontWeight: '500',
194+
lineHeight: '16px',
195+
letterSpacing: '-0.15px',
196+
padding: '12px 8px',
197+
}}
198+
>
199+
{flexRender(
200+
header.column.columnDef.header,
201+
header.getContext()
202+
)}
203+
{canSort && (
204+
<span>
205+
{header.column.getIsSorted() === 'asc'
206+
? ' ▲'
207+
: header.column.getIsSorted() === 'desc'
208+
? ' ▼'
209+
: ''}
210+
</span>
211+
)}
212+
</TableHead>
213+
);
214+
})}
215+
</TableRow>
216+
))}
217+
</TableHeader>
218+
<TableBody
219+
style={{
220+
color: 'var(--sl-color-gray-1)',
221+
fontFamily: 'AeonikFono',
222+
fontSize: '14px',
223+
fontStyle: 'normal',
224+
fontWeight: '400',
225+
lineHeight: '16px',
226+
letterSpacing: '-0.15px',
227+
}}
228+
>
229+
{table.getRowModel().rows.map((row) => (
230+
<TableRow key={row.id}>
231+
{row.getVisibleCells().map((cell) => (
232+
<TableCell
233+
key={cell.id}
234+
style={{
235+
textAlign: cell.column.id === 'operation' ? 'left' : 'center',
236+
border: '1px solid #999CAD',
237+
padding: '12px 8px',
238+
overflow: 'hidden',
239+
textOverflow: 'ellipsis',
240+
whiteSpace:
241+
cell.column.id === 'operation' ? 'normal' : 'nowrap',
242+
}}
243+
>
244+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
245+
</TableCell>
246+
))}
247+
</TableRow>
248+
))}
249+
</TableBody>
250+
</Table>
251+
<div className="flex items-center justify-between mt-4">
252+
<button
253+
className="px-3 py-1 border rounded disabled:opacity-50"
254+
style={{
255+
color: 'var(--sl-color-gray-1)',
256+
fontFamily: 'AeonikFono',
257+
fontSize: '14px',
258+
fontStyle: 'normal',
259+
fontWeight: '500',
260+
lineHeight: '24px',
261+
letterSpacing: '-0.2px',
262+
}}
263+
onClick={() => table.previousPage()}
264+
disabled={!table.getCanPreviousPage()}
265+
>
266+
Previous
267+
</button>
268+
<span>
269+
Page {table.getState().pagination.pageIndex + 1} of{' '}
270+
{table.getPageCount()}
271+
</span>
272+
<button
273+
className="px-3 py-1 border rounded disabled:opacity-50"
274+
style={{
275+
color: 'var(--sl-color-gray-1)',
276+
fontFamily: 'AeonikFono',
277+
fontSize: '14px',
278+
fontStyle: 'normal',
279+
fontWeight: '500',
280+
lineHeight: '24px',
281+
letterSpacing: '-0.2px',
282+
}}
283+
onClick={() => table.nextPage()}
284+
disabled={!table.getCanNextPage()}
285+
>
286+
Next
287+
</button>
288+
</div>
289+
</div>
290+
</div>
291+
</div>
292+
);
293+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: "API Management"
3+
description: API coverage for Microsoft.ApiManagement in LocalStack for Azure.
4+
template: doc
5+
---
6+
7+
import AzureFeatureCoverage from "../../../../components/feature-coverage/AzureFeatureCoverage";
8+
9+
## API Coverage
10+
11+
<AzureFeatureCoverage service="Microsoft.ApiManagement" client:load />
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: "App"
3+
description: API coverage for Microsoft.App in LocalStack for Azure.
4+
template: doc
5+
---
6+
7+
import AzureFeatureCoverage from "../../../../components/feature-coverage/AzureFeatureCoverage";
8+
9+
## API Coverage
10+
11+
<AzureFeatureCoverage service="Microsoft.App" client:load />
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: "Authorization"
3+
description: API coverage for Microsoft.Authorization in LocalStack for Azure.
4+
template: doc
5+
---
6+
7+
import AzureFeatureCoverage from "../../../../components/feature-coverage/AzureFeatureCoverage";
8+
9+
## API Coverage
10+
11+
<AzureFeatureCoverage service="Microsoft.Authorization" client:load />
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
title: "Azure Container Registry"
3+
description: API coverage for Azure.ContainerRegistry in LocalStack for Azure.
4+
template: doc
5+
---
6+
7+
import AzureFeatureCoverage from "../../../../components/feature-coverage/AzureFeatureCoverage";
8+
9+
## API Coverage
10+
11+
<AzureFeatureCoverage service="Azure.ContainerRegistry" client:load />
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
title: "Blob Storage"
3+
description: API coverage for Microsoft.BlobStorage in LocalStack for Azure.
4+
template: doc
5+
---
6+
7+
import AzureFeatureCoverage from "../../../../components/feature-coverage/AzureFeatureCoverage";
8+
9+
## API Coverage
10+
11+
<AzureFeatureCoverage service="Microsoft.BlobStorage" client:load />
12+

0 commit comments

Comments
 (0)