From 0dbca336f86cbd32b30575c79424b86436166e04 Mon Sep 17 00:00:00 2001 From: CD Cabrera Date: Wed, 18 Feb 2026 03:07:20 -0500 Subject: [PATCH] feat(completions): activate mcp resource completions * docs, remove old embedded doc files, favor docs.json * options.defaults, remove old embedded doc refs * resources, activate pf version, section, category completions * resource.patternFlyComponentsIndex, new resource index of components * tool.patternFlyDocs, use centralized pf.getResources, expose pf version for inputs * tool.searchPatternFlyDocs, use centralized pf.search, getResources, expose pf version in results --- jest.setupTests.ts | 23 ++ .../__snapshots__/docs.chart.test.ts.snap | 36 --- .../__snapshots__/docs.component.test.ts.snap | 208 ------------ .../__snapshots__/docs.layout.test.ts.snap | 22 -- .../__snapshots__/docs.local.test.ts.snap | 33 -- .../options.defaults.test.ts.snap | 9 - .../resource.patternFlyDocsIndex.test.ts.snap | 27 +- ...source.patternFlyDocsTemplate.test.ts.snap | 26 +- ...source.patternFlySchemasIndex.test.ts.snap | 23 +- ...rce.patternFlySchemasTemplate.test.ts.snap | 17 +- .../__snapshots__/server.test.ts.snap | 38 +++ .../tool.patternFlyDocs.test.ts.snap | 8 + .../tool.searchPatternFlyDocs.test.ts.snap | 10 +- src/__tests__/docs.chart.test.ts | 8 - src/__tests__/docs.component.test.ts | 8 - src/__tests__/docs.layout.test.ts | 8 - src/__tests__/docs.local.test.ts | 25 -- .../resource.patternFlyDocsIndex.test.ts | 48 +-- .../resource.patternFlyDocsTemplate.test.ts | 160 +++++----- .../resource.patternFlySchemasIndex.test.ts | 26 +- ...resource.patternFlySchemasTemplate.test.ts | 130 ++++---- src/__tests__/server.test.ts | 15 +- src/__tests__/tool.patternFlyDocs.test.ts | 34 +- src/docs.chart.ts | 34 -- src/docs.component.ts | 264 ---------------- src/docs.layout.ts | 20 -- src/docs.local.ts | 20 -- src/options.defaults.ts | 101 ------ src/resource.patternFlyComponentsIndex.ts | 145 +++++++++ src/resource.patternFlyContext.ts | 17 +- src/resource.patternFlyDocsIndex.ts | 280 ++++++++++++++--- src/resource.patternFlyDocsTemplate.ts | 229 +++++++++----- src/resource.patternFlySchemasIndex.ts | 144 ++++++++- src/resource.patternFlySchemasTemplate.ts | 188 +++++++---- src/server.ts | 3 + src/tool.componentSchemas.ts | 29 +- src/tool.patternFlyDocs.ts | 114 ++++--- src/tool.searchPatternFlyDocs.ts | 295 ++++++------------ tests/e2e/httpTransport.test.ts | 3 +- tests/e2e/stdioTransport.test.ts | 3 +- 40 files changed, 1389 insertions(+), 1442 deletions(-) delete mode 100644 src/__tests__/__snapshots__/docs.chart.test.ts.snap delete mode 100644 src/__tests__/__snapshots__/docs.component.test.ts.snap delete mode 100644 src/__tests__/__snapshots__/docs.layout.test.ts.snap delete mode 100644 src/__tests__/__snapshots__/docs.local.test.ts.snap delete mode 100644 src/__tests__/docs.chart.test.ts delete mode 100644 src/__tests__/docs.component.test.ts delete mode 100644 src/__tests__/docs.layout.test.ts delete mode 100644 src/__tests__/docs.local.test.ts delete mode 100644 src/docs.chart.ts delete mode 100644 src/docs.component.ts delete mode 100644 src/docs.layout.ts delete mode 100644 src/docs.local.ts create mode 100644 src/resource.patternFlyComponentsIndex.ts diff --git a/jest.setupTests.ts b/jest.setupTests.ts index 26ed74ab..c1e520c8 100644 --- a/jest.setupTests.ts +++ b/jest.setupTests.ts @@ -1,4 +1,5 @@ // Shared helpers for Jest unit tests +import { readFile } from 'node:fs/promises'; /** * Set NODE_ENV to 'local' for local testing. @@ -22,6 +23,16 @@ jest.mock('pid-port', () => ({ portToPid: jest.fn().mockResolvedValue(undefined) })); +/** + * Note: Mock node:fs/promises `readFile` + */ +jest.mock('node:fs/promises', () => ({ + ...jest.requireActual('node:fs/promises'), + readFile: jest.fn() +})); + +const mockReadFile = readFile as jest.MockedFunction; + /** * Note: Mock @patternfly/patternfly-component-schemas/json to avoid top-level await issues in Jest * - Individual tests can override mock @@ -50,3 +61,15 @@ jest.mock('@patternfly/patternfly-component-schemas/json', () => ({ throw new Error(`Component "${componentName}" not found`); }) }), { virtual: true }); + +let mockFetch: jest.SpyInstance; + +beforeEach(() => { + mockFetch = jest.spyOn(global, 'fetch'); +}); + +afterEach(() => { + mockFetch.mockRestore(); +}); + +export { mockFetch, mockReadFile }; diff --git a/src/__tests__/__snapshots__/docs.chart.test.ts.snap b/src/__tests__/__snapshots__/docs.chart.test.ts.snap deleted file mode 100644 index 1e40b3f5..00000000 --- a/src/__tests__/__snapshots__/docs.chart.test.ts.snap +++ /dev/null @@ -1,36 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`docsChart should return specific properties 1`] = ` -{ - "CHART_DOCS": [ - "[@patternfly/Charts - Colors for Charts - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartTheme/examples/ChartTheme.md)", - "[@patternfly/Charts - Area Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/area-chart/area-chart.md)", - "[@patternfly/Charts - Area Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartArea/examples/ChartArea.md)", - "[@patternfly/Charts - Bar Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/bar-chart/bar-chart.md)", - "[@patternfly/Charts - Bar Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartBar/examples/ChartBar.md)", - "[@patternfly/Charts - Box Plot Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartBoxPlot/examples/ChartBoxPlot.md)", - "[@patternfly/Charts - Bullet Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/bullet-chart/bullet-chart.md)", - "[@patternfly/Charts - Bullet Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartBullet/examples/ChartBullet.md)", - "[@patternfly/Charts - Donut Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/donut-chart/donut-chart.md)", - "[@patternfly/Charts - Donut Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartDonut/examples/ChartDonut.md)", - "[@patternfly/Charts - Donut Utilization Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/donut-utilization-chart/donut-utilization-chart.md)", - "[@patternfly/Charts - Donut Utilization Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartDonutUtilization/examples/ChartDonutUtilization.md)", - "[@patternfly/Charts - Line Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/line-chart/line-chart.md)", - "[@patternfly/Charts - Line Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartLine/examples/ChartLine.md)", - "[@patternfly/Charts - Pie Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/pie-chart/pie-chart.md)", - "[@patternfly/Charts - Pie Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartPie/examples/ChartPie.md)", - "[@patternfly/Charts - Scatter Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/scatter-chart/scatter-chart.md)", - "[@patternfly/Charts - Scatter Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartScatter/examples/ChartScatter.md)", - "[@patternfly/Charts - Sparkline Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/sparkline-chart/sparkline-chart.md)", - "[@patternfly/Charts - Sparkline Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/Sparkline/examples/sparkline.md)", - "[@patternfly/Charts - Stack Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/stacked-chart/stacked-chart.md)", - "[@patternfly/Charts - Stack Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartStack/examples/ChartStack.md)", - "[@patternfly/Charts - Threshold Chart - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/threshold-chart/threshold-chart.md)", - "[@patternfly/Charts - Threshold Chart - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartThreshold/examples/ChartThreshold.md)", - "[@patternfly/Charts - Legend - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/legend-chart/legend-chart.md)", - "[@patternfly/Charts - Legend - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartLegend/examples/ChartLegend.md)", - "[@patternfly/Charts - Tooltip - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts/tooltip-chart/tooltip-chart.md)", - "[@patternfly/Charts - Tooltip - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components/ChartTooltip/examples/ChartTooltip.md)", - ], -} -`; diff --git a/src/__tests__/__snapshots__/docs.component.test.ts.snap b/src/__tests__/__snapshots__/docs.component.test.ts.snap deleted file mode 100644 index c4207397..00000000 --- a/src/__tests__/__snapshots__/docs.component.test.ts.snap +++ /dev/null @@ -1,208 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`docsComponent should return specific properties 1`] = ` -{ - "COMPONENT_DOCS": [ - "[@patternfly/AboutModal - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/about-modal/about-modal.md)", - "[@patternfly/AboutModal - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/about-modal/about-modal.md)", - "[@patternfly/AboutModal - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/AboutModal/examples/AboutModal.md)", - "[@patternfly/Accordion - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/accordion/accordion.md)", - "[@patternfly/Accordion - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/accordion/accordion.md)", - "[@patternfly/Accordion - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Accordion/examples/Accordion.md)", - "[@patternfly/ActionList - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/action-list/action-list.md)", - "[@patternfly/ActionList - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/action-list/action-list.md)", - "[@patternfly/ActionList - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/ActionList/examples/ActionList.md)", - "[@patternfly/Alert - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/alert/alert.md)", - "[@patternfly/Alert - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/alert/alert.md)", - "[@patternfly/Alert - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Alert/examples/Alert.md)", - "[@patternfly/ApplicationLauncher - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/application-launcher/application-launcher.md)", - "[@patternfly/ApplicationLauncher - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/application-launcher/application-launcher.md)", - "[@patternfly/Avatar - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/avatar/avatar.md)", - "[@patternfly/Avatar - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/avatar/avatar.md)", - "[@patternfly/Avatar - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Avatar/examples/Avatar.md)", - "[@patternfly/BackToTop - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/back-to-top/back-to-top.md)", - "[@patternfly/BackToTop - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/BackToTop/examples/BackToTop.md)", - "[@patternfly/Backdrop - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/backdrop/backdrop.md)", - "[@patternfly/Backdrop - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/backdrop/backdrop.md)", - "[@patternfly/Backdrop - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Backdrop/examples/Backdrop.md)", - "[@patternfly/BackgroundImage - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/background-image/background-image.md)", - "[@patternfly/BackgroundImage - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/background-image/background-image.md)", - "[@patternfly/BackgroundImage - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/BackgroundImage/examples/BackgroundImage.md)", - "[@patternfly/Badge - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/badge/badge.md)", - "[@patternfly/Badge - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/badge/badge.md)", - "[@patternfly/Badge - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Badge/examples/Badge.md)", - "[@patternfly/Banner - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/banner/banner.md)", - "[@patternfly/Banner - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/banner/banner.md)", - "[@patternfly/Banner - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Banner/examples/Banner.md)", - "[@patternfly/Brand - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/brand/brand.md)", - "[@patternfly/Brand - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/brand/brand.md)", - "[@patternfly/Brand - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Brand/examples/Brand.md)", - "[@patternfly/Breadcrumb - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/breadcrumb/breadcrumb.md)", - "[@patternfly/Breadcrumb - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/breadcrumb/breadcrumb.md)", - "[@patternfly/Breadcrumb - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Breadcrumb/examples/Breadcrumb.md)", - "[@patternfly/Button - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/button/button.md)", - "[@patternfly/Button - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/button/button.md)", - "[@patternfly/Button - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Button/examples/Button.md)", - "[@patternfly/CalendarMonth - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/calendar-month/calendar-month.md)", - "[@patternfly/CalendarMonth - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/calendar-month/calendar-month.md)", - "[@patternfly/CalendarMonth - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/CalendarMonth/examples/CalendarMonth.md)", - "[@patternfly/Card - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/card/card.md)", - "[@patternfly/Card - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/card/card.md)", - "[@patternfly/Card - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Card/examples/Card.md)", - "[@patternfly/Checkbox - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/checkbox/checkbox.md)", - "[@patternfly/Checkbox - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/checkbox/checkbox.md)", - "[@patternfly/Checkbox - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Checkbox/examples/Checkbox.md)", - "[@patternfly/ChipDeprecated - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/chip/chip.md)", - "[@patternfly/ChipDeprecated - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/chip/chip.md)", - "[@patternfly/ClipboardCopy - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/clipboard-copy/clipboard-copy.md)", - "[@patternfly/ClipboardCopy - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/clipboard-copy/clipboard-copy.md)", - "[@patternfly/ClipboardCopy - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/ClipboardCopy/examples/ClipboardCopy.md)", - "[@patternfly/CodeBlock - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/code-block/code-block.md)", - "[@patternfly/CodeBlock - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/CodeBlock/examples/CodeBlock.md)", - "[@patternfly/CodeEditor - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/code-editor/code-editor.md)", - "[@patternfly/CodeEditor - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/code-editor/code-editor.md)", - "[@patternfly/Content - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/content/content.md)", - "[@patternfly/Content - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Content/examples/Content.md)", - "[@patternfly/DataList - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/data-list/data-list.md)", - "[@patternfly/DataList - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/DataList/examples/DataList.md)", - "[@patternfly/DatePicker - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/date-picker/date-picker.md)", - "[@patternfly/DatePicker - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/DatePicker/examples/DatePicker.md)", - "[@patternfly/DescriptionList - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/description-list/description-list.md)", - "[@patternfly/DescriptionList - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/DescriptionList/examples/DescriptionList.md)", - "[@patternfly/Divider - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/divider/divider.md)", - "[@patternfly/Divider - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Divider/examples/Divider.md)", - "[@patternfly/DragAndDrop - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/drag-and-drop/drag.md)", - "[@patternfly/Drawer - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/drawer/drawer.md)", - "[@patternfly/Drawer - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Drawer/examples/Drawer.md)", - "[@patternfly/Dropdown - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/dropdown/dropdown.md)", - "[@patternfly/Dropdown - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Dropdown/examples/Dropdown.md)", - "[@patternfly/DualListSelector - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/dual-list-selector/dual-list-selector.md)", - "[@patternfly/DualListSelector - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/DualListSelector/examples/DualListSelector.md)", - "[@patternfly/EmptyState - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/empty-state/empty-state.md)", - "[@patternfly/EmptyState - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/EmptyState/examples/EmptyState.md)", - "[@patternfly/ExpandableSection - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/expandable-section/expandable-section.md)", - "[@patternfly/ExpandableSection - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/expandable-section/expandable-section.md)", - "[@patternfly/ExpandableSection - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/ExpandableSection/examples/ExpandableSection.md)", - "[@patternfly/FileUpload - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/file-upload/file-upload.md)", - "[@patternfly/FileUpload - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/FileUpload/examples/FileUpload.md)", - "[@patternfly/Form - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/form/forms.md)", - "[@patternfly/Form - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Form/examples/Form.md)", - "[@patternfly/FormControl - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/form-control/form-control.md)", - "[@patternfly/FormSelect - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/form-select/form-select.md)", - "[@patternfly/FormSelect - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/FormSelect/examples/FormSelect.md)", - "[@patternfly/HelperText - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/helper-text/helper-text.md)", - "[@patternfly/HelperText - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/helper-text/helper-text.md)", - "[@patternfly/HelperText - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/HelperText/examples/HelperText.md)", - "[@patternfly/Hint - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/hint/hint.md)", - "[@patternfly/Hint - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Hint/examples/Hint.md)", - "[@patternfly/Icon - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Icon/examples/Icon.md)", - "[@patternfly/InlineEdit - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/inline-edit/inline-edit.md)", - "[@patternfly/InputGroup - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/input-group/input-group.md)", - "[@patternfly/InputGroup - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/InputGroup/examples/InputGroup.md)", - "[@patternfly/JumpLinks - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/jump-link/jump-link.md)", - "[@patternfly/JumpLinks - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/jump-links/jump-links.md)", - "[@patternfly/JumpLinks - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/JumpLinks/examples/JumpLinks.md)", - "[@patternfly/Label - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/label/label.md)", - "[@patternfly/Label - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/label/label.md)", - "[@patternfly/Label - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Label/examples/Label.md)", - "[@patternfly/List - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/list/list.md)", - "[@patternfly/List - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/List/examples/List.md)", - "[@patternfly/LoginPage - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/login-page/login-page.md)", - "[@patternfly/LoginPage - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/LoginPage/examples/LoginPage.md)", - "[@patternfly/Masthead - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/masthead/masthead.md)", - "[@patternfly/Masthead - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Masthead/examples/Masthead.md)", - "[@patternfly/Menu - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/menu/menu.md)", - "[@patternfly/Menu - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/menu/menu.md)", - "[@patternfly/Menu - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Menu/examples/Menu.md)", - "[@patternfly/MenuToggle - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/menu-toggle/menu-toggle.md)", - "[@patternfly/MenuToggle - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/menu-toggle/menu-toggle.md)", - "[@patternfly/MenuToggle - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/MenuToggle/examples/MenuToggle.md)", - "[@patternfly/Modal - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/modal/modal.md)", - "[@patternfly/Modal - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/modal/modal.md)", - "[@patternfly/Modal - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Modal/examples/Modal.md)", - "[@patternfly/Navigation - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/navigation/navigation.md)", - "[@patternfly/Navigation - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/navigation/navigation.md)", - "[@patternfly/NotificationBadge - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/notification-badge/notification-badge.md)", - "[@patternfly/NotificationBadge - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/NotificationBadge/examples/NotificationBadge.md)", - "[@patternfly/NotificationDrawer - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/notification-drawer/notification-drawer.md)", - "[@patternfly/NotificationDrawer - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/NotificationDrawer/examples/NotificationDrawer.md)", - "[@patternfly/NumberInput - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/number%20input/number-input.md)", - "[@patternfly/NumberInput - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/NumberInput/examples/NumberInput.md)", - "[@patternfly/OverflowMenu - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/overflow-menu/overflow-menu.md)", - "[@patternfly/OverflowMenu - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/OverflowMenu/examples/OverflowMenu.md)", - "[@patternfly/Page - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/page/page.md)", - "[@patternfly/Page - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/page/page.md)", - "[@patternfly/Page - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Page/examples/Page.md)", - "[@patternfly/Pagination - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/pagination/pagination.md)", - "[@patternfly/Pagination - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Pagination/examples/Pagination.md)", - "[@patternfly/Panel - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/panel/panel.md)", - "[@patternfly/Panel - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Panel/examples/Panel.md)", - "[@patternfly/Popover - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/popover/popover.md)", - "[@patternfly/Popover - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Popover/examples/Popover.md)", - "[@patternfly/Progress - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/progress/progress.md)", - "[@patternfly/Progress - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/progress/progress.md)", - "[@patternfly/Progress - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Progress/examples/Progress.md)", - "[@patternfly/ProgressStepper - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/progress-stepper/progress-stepper.md)", - "[@patternfly/ProgressStepper - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/progress-stepper/progress-stepper.md)", - "[@patternfly/ProgressStepper - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/ProgressStepper/examples/ProgressStepper.md)", - "[@patternfly/Radio - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/radio/radio.md)", - "[@patternfly/Radio - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/radio/radio.md)", - "[@patternfly/Radio - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Radio/examples/Radio.md)", - "[@patternfly/SearchInput - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/search-input/search-input.md)", - "[@patternfly/SearchInput - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/SearchInput/examples/SearchInput.md)", - "[@patternfly/Select - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/select/select.md)", - "[@patternfly/Select - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Select/examples/Select.md)", - "[@patternfly/Sidebar - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/sidebar/sidebar.md)", - "[@patternfly/Sidebar - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/sidebar/sidebar.md)", - "[@patternfly/Sidebar - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Sidebar/examples/Sidebar.md)", - "[@patternfly/SimpleList - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/simple-list/simple-list.md)", - "[@patternfly/SimpleList - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/SimpleList/examples/SimpleList.md)", - "[@patternfly/Skeleton - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/skeleton/skeleton.md)", - "[@patternfly/Skeleton - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/skeleton/skeleton.md)", - "[@patternfly/Skeleton - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Skeleton/examples/Skeleton.md)", - "[@patternfly/SkipToContent - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/skip-to-content/skip-to-content.md)", - "[@patternfly/SkipToContent - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/skip-to-content/skip-to-content.md)", - "[@patternfly/SkipToContent - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/SkipToContent/examples/SkipToContent.md)", - "[@patternfly/Slider - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/slider/slider.md)", - "[@patternfly/Slider - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Slider/examples/Slider.md)", - "[@patternfly/Spinner - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/spinner/spinner.md)", - "[@patternfly/Spinner - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Spinner/examples/Spinner.md)", - "[@patternfly/Switch - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/switch/switch.md)", - "[@patternfly/Switch - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/switch/switch.md)", - "[@patternfly/Switch - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Switch/examples/Switch.md)", - "[@patternfly/Table - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/table/table.md)", - "[@patternfly/Table - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-table/src/components/Table/examples/Table.md)", - "[@patternfly/Tabs - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/tabs/tabs.md)", - "[@patternfly/Tabs - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/tabs/tabs.md)", - "[@patternfly/Tabs - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Tabs/examples/Tabs.md)", - "[@patternfly/TextArea - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/text-area/text-area.md)", - "[@patternfly/TextArea - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/TextArea/examples/TextArea.md)", - "[@patternfly/TextInput - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/text-input/text-input.md)", - "[@patternfly/TextInput - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/TextInput/examples/TextInput.md)", - "[@patternfly/TextInputGroup - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/text-input-group/text-input-group.md)", - "[@patternfly/TextInputGroup - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/TextInputGroup/examples/TextInputGroup.md)", - "[@patternfly/TileDeprecated - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/tile/tile.md)", - "[@patternfly/TimePicker - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/time%20picker/time-picker.md)", - "[@patternfly/TimePicker - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/TimePicker/examples/TimePicker.md)", - "[@patternfly/Timestamp - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/timestamp/timestamp.md)", - "[@patternfly/Timestamp - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Timestamp/examples/Timestamp.md)", - "[@patternfly/Title - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/title/title.md)", - "[@patternfly/Title - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/title/title.md)", - "[@patternfly/Title - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Title/examples/Title.md)", - "[@patternfly/ToggleGroup - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/toggle-group/toggle-group.md)", - "[@patternfly/ToggleGroup - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/ToggleGroup/examples/ToggleGroup.md)", - "[@patternfly/Toolbar - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/toolbar/toolbar.md)", - "[@patternfly/Toolbar - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Toolbar/examples/Toolbar.md)", - "[@patternfly/Tooltip - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/tooltip/tooltip.md)", - "[@patternfly/Tooltip - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/tooltip/tooltip.md)", - "[@patternfly/Tooltip - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Tooltip/examples/Tooltip.md)", - "[@patternfly/TreeView - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/tree-view/tree-view.md)", - "[@patternfly/TreeView - Accessibility](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility/tree-view/tree-view.md)", - "[@patternfly/TreeView - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/TreeView/examples/TreeView.md)", - "[@patternfly/Truncate - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/truncate/truncate.md)", - "[@patternfly/Truncate - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Truncate/examples/Truncate.md)", - "[@patternfly/Wizard - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components/wizard/wizard.md)", - "[@patternfly/Wizard - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components/Wizard/examples/Wizard.md)", - ], -} -`; diff --git a/src/__tests__/__snapshots__/docs.layout.test.ts.snap b/src/__tests__/__snapshots__/docs.layout.test.ts.snap deleted file mode 100644 index 5fa8be77..00000000 --- a/src/__tests__/__snapshots__/docs.layout.test.ts.snap +++ /dev/null @@ -1,22 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`docsLayout should return specific properties 1`] = ` -{ - "LAYOUT_DOCS": [ - "[@patternfly/Bullseye - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/layouts/bullseye.md)", - "[@patternfly/Bullseye - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/layouts/Bullseye/examples/Bullseye.md)", - "[@patternfly/Flex - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/layouts/flex.md)", - "[@patternfly/Flex - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/layouts/Flex/examples/Flex.md)", - "[@patternfly/Gallery - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/layouts/gallery.md)", - "[@patternfly/Gallery - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/layouts/Gallery/examples/Gallery.md)", - "[@patternfly/Grid - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/layouts/grid.md)", - "[@patternfly/Grid - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/layouts/Grid/examples/Grid.md)", - "[@patternfly/Level - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/layouts/level.md)", - "[@patternfly/Level - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/layouts/Level/examples/Level.md)", - "[@patternfly/Split - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/layouts/split.md)", - "[@patternfly/Split - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/layouts/Split/examples/Split.md)", - "[@patternfly/Stack - Design Guidelines](https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/layouts/stack.md)", - "[@patternfly/Stack - Examples](https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/layouts/Stack/examples/Stack.md)", - ], -} -`; diff --git a/src/__tests__/__snapshots__/docs.local.test.ts.snap b/src/__tests__/__snapshots__/docs.local.test.ts.snap deleted file mode 100644 index d0d320cc..00000000 --- a/src/__tests__/__snapshots__/docs.local.test.ts.snap +++ /dev/null @@ -1,33 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`docsLocal should return specific properties 1`] = ` -{ - "getLocalDocs": [Function], -} -`; - -exports[`getLocalDocs should return local references when called, default 1`] = ` -[ - "[@patternfly/react-charts](/documentation/charts/README.md)", - "[@patternfly/react-chatbot](/documentation/chatbot/README.md)", - "[@patternfly/react-component-groups](/documentation/component-groups/README.md)", - "[@patternfly/react-components](/documentation/components/README.md)", - "[@patternfly/react-guidelines](/documentation/guidelines/README.md)", - "[@patternfly/react-resources](/documentation/resources/README.md)", - "[@patternfly/react-setup](/documentation/setup/README.md)", - "[@patternfly/react-troubleshooting](/documentation/troubleshooting/README.md)", -] -`; - -exports[`getLocalDocs should return local references when called, with custom docsPath 1`] = ` -[ - "[@patternfly/react-charts](custom/docs/path/charts/README.md)", - "[@patternfly/react-chatbot](custom/docs/path/chatbot/README.md)", - "[@patternfly/react-component-groups](custom/docs/path/component-groups/README.md)", - "[@patternfly/react-components](custom/docs/path/components/README.md)", - "[@patternfly/react-guidelines](custom/docs/path/guidelines/README.md)", - "[@patternfly/react-resources](custom/docs/path/resources/README.md)", - "[@patternfly/react-setup](custom/docs/path/setup/README.md)", - "[@patternfly/react-troubleshooting](custom/docs/path/troubleshooting/README.md)", -] -`; diff --git a/src/__tests__/__snapshots__/options.defaults.test.ts.snap b/src/__tests__/__snapshots__/options.defaults.test.ts.snap index 2163e1be..bc4cff20 100644 --- a/src/__tests__/__snapshots__/options.defaults.test.ts.snap +++ b/src/__tests__/__snapshots__/options.defaults.test.ts.snap @@ -53,15 +53,6 @@ exports[`options defaults should return specific properties: defaults 1`] = ` ], }, }, - "pfExternal": "https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content", - "pfExternalAccessibility": "https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/accessibility", - "pfExternalChartsDesign": "https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/charts", - "pfExternalDesignComponents": "https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/components", - "pfExternalDesignLayouts": "https://raw.githubusercontent.com/patternfly/patternfly-org/fb05713aba75998b5ecf5299ee3c1a259119bd74/packages/documentation-site/patternfly-docs/content/design-guidelines/layouts", - "pfExternalExamplesCharts": "https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-charts/src/victory/components", - "pfExternalExamplesComponents": "https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/components", - "pfExternalExamplesLayouts": "https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-core/src/layouts", - "pfExternalExamplesTable": "https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/v6.4.0/packages/react-table/src/components", "pluginHost": { "gracePeriodMs": 2000, "invokeTimeoutMs": 10000, diff --git a/src/__tests__/__snapshots__/resource.patternFlyDocsIndex.test.ts.snap b/src/__tests__/__snapshots__/resource.patternFlyDocsIndex.test.ts.snap index ad9fd094..7998fb18 100644 --- a/src/__tests__/__snapshots__/resource.patternFlyDocsIndex.test.ts.snap +++ b/src/__tests__/__snapshots__/resource.patternFlyDocsIndex.test.ts.snap @@ -5,6 +5,31 @@ exports[`patternFlyDocsIndexResource should have a consistent return structure: "config": true, "handler": [Function], "name": "patternfly-docs-index", - "uri": "patternfly://docs/index", + "uri": ResourceTemplate { + "_callbacks": { + "complete": { + "category": [Function], + "section": [Function], + "version": [Function], + }, + "list": [Function], + }, + "_uriTemplate": UriTemplate { + "parts": [ + "patternfly://docs/index", + { + "exploded": false, + "name": "version", + "names": [ + "version", + "section", + "category", + ], + "operator": "?", + }, + ], + "template": "patternfly://docs/index{?version,section,category}", + }, + }, } `; diff --git a/src/__tests__/__snapshots__/resource.patternFlyDocsTemplate.test.ts.snap b/src/__tests__/__snapshots__/resource.patternFlyDocsTemplate.test.ts.snap index 5ed0afb0..672bbf2c 100644 --- a/src/__tests__/__snapshots__/resource.patternFlyDocsTemplate.test.ts.snap +++ b/src/__tests__/__snapshots__/resource.patternFlyDocsTemplate.test.ts.snap @@ -7,11 +7,24 @@ exports[`patternFlyDocsTemplateResource should have a consistent return structur "name": "patternfly-docs-template", "uri": ResourceTemplate { "_callbacks": { - "list": undefined, + "complete": { + "name": [Function], + "version": [Function], + }, + "list": [Function], }, "_uriTemplate": UriTemplate { "parts": [ "patternfly://docs/", + { + "exploded": false, + "name": "version", + "names": [ + "version", + ], + "operator": "", + }, + "/", { "exploded": false, "name": "name", @@ -20,8 +33,17 @@ exports[`patternFlyDocsTemplateResource should have a consistent return structur ], "operator": "", }, + { + "exploded": false, + "name": "section", + "names": [ + "section", + "category", + ], + "operator": "?", + }, ], - "template": "patternfly://docs/{name}", + "template": "patternfly://docs/{version}/{name}{?section,category}", }, }, } diff --git a/src/__tests__/__snapshots__/resource.patternFlySchemasIndex.test.ts.snap b/src/__tests__/__snapshots__/resource.patternFlySchemasIndex.test.ts.snap index 26bd6c35..500275a4 100644 --- a/src/__tests__/__snapshots__/resource.patternFlySchemasIndex.test.ts.snap +++ b/src/__tests__/__snapshots__/resource.patternFlySchemasIndex.test.ts.snap @@ -5,6 +5,27 @@ exports[`patternFlySchemasIndexResource should have a consistent return structur "config": true, "handler": [Function], "name": "patternfly-schemas-index", - "uri": "patternfly://schemas/index", + "uri": ResourceTemplate { + "_callbacks": { + "complete": { + "version": [Function], + }, + "list": [Function], + }, + "_uriTemplate": UriTemplate { + "parts": [ + "patternfly://schemas/index", + { + "exploded": false, + "name": "version", + "names": [ + "version", + ], + "operator": "?", + }, + ], + "template": "patternfly://schemas/index{?version}", + }, + }, } `; diff --git a/src/__tests__/__snapshots__/resource.patternFlySchemasTemplate.test.ts.snap b/src/__tests__/__snapshots__/resource.patternFlySchemasTemplate.test.ts.snap index 529dcd1b..32cd8227 100644 --- a/src/__tests__/__snapshots__/resource.patternFlySchemasTemplate.test.ts.snap +++ b/src/__tests__/__snapshots__/resource.patternFlySchemasTemplate.test.ts.snap @@ -7,11 +7,24 @@ exports[`patternFlySchemasTemplateResource should have a consistent return struc "name": "patternfly-schemas-template", "uri": ResourceTemplate { "_callbacks": { - "list": undefined, + "complete": { + "name": [Function], + "version": [Function], + }, + "list": [Function], }, "_uriTemplate": UriTemplate { "parts": [ "patternfly://schemas/", + { + "exploded": false, + "name": "version", + "names": [ + "version", + ], + "operator": "", + }, + "/", { "exploded": false, "name": "name", @@ -21,7 +34,7 @@ exports[`patternFlySchemasTemplateResource should have a consistent return struc "operator": "", }, ], - "template": "patternfly://schemas/{name}", + "template": "patternfly://schemas/{version}/{name}", }, }, } diff --git a/src/__tests__/__snapshots__/server.test.ts.snap b/src/__tests__/__snapshots__/server.test.ts.snap index d9331350..ec958945 100644 --- a/src/__tests__/__snapshots__/server.test.ts.snap +++ b/src/__tests__/__snapshots__/server.test.ts.snap @@ -18,6 +18,9 @@ exports[`runServer should allow server to be stopped, http stop server: diagnost [ "Registered resource: patternfly-context", ], + [ + "Registered resource: patternfly-components-index", + ], [ "Registered resource: patternfly-docs-index", ], @@ -71,6 +74,9 @@ exports[`runServer should allow server to be stopped, stdio stop server: diagnos [ "Registered resource: patternfly-context", ], + [ + "Registered resource: patternfly-components-index", + ], [ "Registered resource: patternfly-docs-index", ], @@ -124,6 +130,9 @@ exports[`runServer should attempt to run server, create transport, connect, and [ "Registered resource: patternfly-context", ], + [ + "Registered resource: patternfly-components-index", + ], [ "Registered resource: patternfly-docs-index", ], @@ -149,6 +158,7 @@ exports[`runServer should attempt to run server, create transport, connect, and }, { "capabilities": { + "completions": {}, "resources": {}, "tools": {}, }, @@ -183,6 +193,9 @@ exports[`runServer should attempt to run server, disable SIGINT handler: diagnos [ "Registered resource: patternfly-context", ], + [ + "Registered resource: patternfly-components-index", + ], [ "Registered resource: patternfly-docs-index", ], @@ -208,6 +221,7 @@ exports[`runServer should attempt to run server, disable SIGINT handler: diagnos }, { "capabilities": { + "completions": {}, "resources": {}, "tools": {}, }, @@ -237,6 +251,9 @@ exports[`runServer should attempt to run server, enable SIGINT handler explicitl [ "Registered resource: patternfly-context", ], + [ + "Registered resource: patternfly-components-index", + ], [ "Registered resource: patternfly-docs-index", ], @@ -262,6 +279,7 @@ exports[`runServer should attempt to run server, enable SIGINT handler explicitl }, { "capabilities": { + "completions": {}, "resources": {}, "tools": {}, }, @@ -296,6 +314,9 @@ exports[`runServer should attempt to run server, register a tool: diagnostics 1` [ "Registered resource: patternfly-context", ], + [ + "Registered resource: patternfly-components-index", + ], [ "Registered resource: patternfly-docs-index", ], @@ -330,6 +351,7 @@ exports[`runServer should attempt to run server, register a tool: diagnostics 1` }, { "capabilities": { + "completions": {}, "resources": {}, "tools": {}, }, @@ -366,6 +388,9 @@ exports[`runServer should attempt to run server, register multiple tools: diagno [ "Registered resource: patternfly-context", ], + [ + "Registered resource: patternfly-components-index", + ], [ "Registered resource: patternfly-docs-index", ], @@ -409,6 +434,7 @@ exports[`runServer should attempt to run server, register multiple tools: diagno }, { "capabilities": { + "completions": {}, "resources": {}, "tools": {}, }, @@ -446,6 +472,9 @@ exports[`runServer should attempt to run server, use custom options: diagnostics [ "Registered resource: patternfly-context", ], + [ + "Registered resource: patternfly-components-index", + ], [ "Registered resource: patternfly-docs-index", ], @@ -471,6 +500,7 @@ exports[`runServer should attempt to run server, use custom options: diagnostics }, { "capabilities": { + "completions": {}, "resources": {}, "tools": {}, }, @@ -505,6 +535,9 @@ exports[`runServer should attempt to run server, use default tools, http: diagno [ "Registered resource: patternfly-context", ], + [ + "Registered resource: patternfly-components-index", + ], [ "Registered resource: patternfly-docs-index", ], @@ -539,6 +572,7 @@ exports[`runServer should attempt to run server, use default tools, http: diagno }, { "capabilities": { + "completions": {}, "resources": {}, "tools": {}, }, @@ -577,6 +611,9 @@ exports[`runServer should attempt to run server, use default tools, stdio: diagn [ "Registered resource: patternfly-context", ], + [ + "Registered resource: patternfly-components-index", + ], [ "Registered resource: patternfly-docs-index", ], @@ -611,6 +648,7 @@ exports[`runServer should attempt to run server, use default tools, stdio: diagn }, { "capabilities": { + "completions": {}, "resources": {}, "tools": {}, }, diff --git a/src/__tests__/__snapshots__/tool.patternFlyDocs.test.ts.snap b/src/__tests__/__snapshots__/tool.patternFlyDocs.test.ts.snap index 7a83354a..0a324259 100644 --- a/src/__tests__/__snapshots__/tool.patternFlyDocs.test.ts.snap +++ b/src/__tests__/__snapshots__/tool.patternFlyDocs.test.ts.snap @@ -7,3 +7,11 @@ exports[`usePatternFlyDocsTool should have a consistent return structure: struct "schema": true, } `; + +exports[`usePatternFlyDocsTool, callback should attempt to parse parameters, multiple files, mock paths 1`] = `"# Content for components/button.md"`; + +exports[`usePatternFlyDocsTool, callback should attempt to parse parameters, single file, mock path 1`] = `"# Content for components/button.md"`; + +exports[`usePatternFlyDocsTool, callback should attempt to parse parameters, with invalid urlList 1`] = `"# Content for invalid-path"`; + +exports[`usePatternFlyDocsTool, callback should attempt to parse parameters, with name and actual path 1`] = `"# Documentation for React Chatbot [AI Guidance]"`; diff --git a/src/__tests__/__snapshots__/tool.searchPatternFlyDocs.test.ts.snap b/src/__tests__/__snapshots__/tool.searchPatternFlyDocs.test.ts.snap index e64429e5..70f797b7 100644 --- a/src/__tests__/__snapshots__/tool.searchPatternFlyDocs.test.ts.snap +++ b/src/__tests__/__snapshots__/tool.searchPatternFlyDocs.test.ts.snap @@ -10,19 +10,19 @@ exports[`searchPatternFlyDocsTool should have a consistent return structure: str exports[`searchPatternFlyDocsTool, callback should parse parameters, default: search 1`] = `"# Search results for "Button", 1 matches found:"`; -exports[`searchPatternFlyDocsTool, callback should parse parameters, with "*" searchQuery all: search 1`] = `"# Search results for "all components", 8 matches found:"`; +exports[`searchPatternFlyDocsTool, callback should parse parameters, with "*" searchQuery all: search 1`] = `"# Search results for "all resources", 112 matches found:"`; -exports[`searchPatternFlyDocsTool, callback should parse parameters, with "all" searchQuery all: search 1`] = `"# Search results for "all components", 8 matches found:"`; +exports[`searchPatternFlyDocsTool, callback should parse parameters, with "all" searchQuery all: search 1`] = `"# Search results for "all resources", 112 matches found:"`; -exports[`searchPatternFlyDocsTool, callback should parse parameters, with empty searchQuery all: search 1`] = `"# Search results for "all components", 8 matches found:"`; +exports[`searchPatternFlyDocsTool, callback should parse parameters, with empty searchQuery all: search 1`] = `"# Search results for "all resources", 112 matches found:"`; exports[`searchPatternFlyDocsTool, callback should parse parameters, with lower case componentName: search 1`] = `"# Search results for "button", 1 matches found:"`; -exports[`searchPatternFlyDocsTool, callback should parse parameters, with made up componentName: search 1`] = `"No PatternFly documentation found matching "lorem ipsum dolor sit amet""`; +exports[`searchPatternFlyDocsTool, callback should parse parameters, with made up componentName: search 1`] = `"No PatternFly resources found matching "lorem ipsum dolor sit amet""`; exports[`searchPatternFlyDocsTool, callback should parse parameters, with multiple words: search 1`] = `"# Search results for "Button Card Table", 3 matches found:"`; -exports[`searchPatternFlyDocsTool, callback should parse parameters, with partial componentName: search 1`] = `"# Search results for "ton", 2 matches found:"`; +exports[`searchPatternFlyDocsTool, callback should parse parameters, with partial componentName: search 1`] = `"# Search results for "ton", 8 matches found:"`; exports[`searchPatternFlyDocsTool, callback should parse parameters, with trimmed componentName: search 1`] = `"# Search results for " Button ", 1 matches found:"`; diff --git a/src/__tests__/docs.chart.test.ts b/src/__tests__/docs.chart.test.ts deleted file mode 100644 index 7eceb123..00000000 --- a/src/__tests__/docs.chart.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as docsChart from '../docs.chart'; - -describe('docsChart', () => { - it('should return specific properties', () => { - expect(docsChart).toMatchSnapshot(); - }); -}); - diff --git a/src/__tests__/docs.component.test.ts b/src/__tests__/docs.component.test.ts deleted file mode 100644 index 9bc21302..00000000 --- a/src/__tests__/docs.component.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as docsComponent from '../docs.component'; - -describe('docsComponent', () => { - it('should return specific properties', () => { - expect(docsComponent).toMatchSnapshot(); - }); -}); - diff --git a/src/__tests__/docs.layout.test.ts b/src/__tests__/docs.layout.test.ts deleted file mode 100644 index 3e848e56..00000000 --- a/src/__tests__/docs.layout.test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as docsLayout from '../docs.layout'; - -describe('docsLayout', () => { - it('should return specific properties', () => { - expect(docsLayout).toMatchSnapshot(); - }); -}); - diff --git a/src/__tests__/docs.local.test.ts b/src/__tests__/docs.local.test.ts deleted file mode 100644 index bba032b8..00000000 --- a/src/__tests__/docs.local.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as docsLocal from '../docs.local'; -import { getLocalDocs } from '../docs.local'; -import { type GlobalOptions } from '../options'; - -describe('docsLocal', () => { - it('should return specific properties', () => { - expect(docsLocal).toMatchSnapshot(); - }); -}); - -describe('getLocalDocs', () => { - it.each([ - { - description: 'default', - options: undefined - }, - { - description: 'with custom docsPath', - options: { docsPath: 'custom/docs/path' } - } - ])('should return local references when called, $description', ({ options }) => { - expect(getLocalDocs(options as GlobalOptions)).toMatchSnapshot(); - }); -}); - diff --git a/src/__tests__/resource.patternFlyDocsIndex.test.ts b/src/__tests__/resource.patternFlyDocsIndex.test.ts index c2bd15ca..94681f00 100644 --- a/src/__tests__/resource.patternFlyDocsIndex.test.ts +++ b/src/__tests__/resource.patternFlyDocsIndex.test.ts @@ -1,18 +1,8 @@ -import { patternFlyDocsIndexResource } from '../resource.patternFlyDocsIndex'; -import { getLocalDocs } from '../docs.local'; +import { patternFlyDocsIndexResource, listResources, uriVersionComplete } from '../resource.patternFlyDocsIndex'; import { isPlainObject } from '../server.helpers'; - -// Mock dependencies -jest.mock('../docs.local'); - -const mockGetLocalDocs = getLocalDocs as jest.MockedFunction; +import { DEFAULT_OPTIONS } from '../options.defaults'; describe('patternFlyDocsIndexResource', () => { - beforeEach(() => { - jest.clearAllMocks(); - mockGetLocalDocs.mockReturnValue(['[@patternfly/react-guidelines](./guidelines/README.md)']); - }); - it('should have a consistent return structure', () => { const resource = patternFlyDocsIndexResource(); @@ -25,23 +15,43 @@ describe('patternFlyDocsIndexResource', () => { }); }); -describe('patternFlyDocsIndexResource, callback', () => { - beforeEach(() => { - jest.clearAllMocks(); - mockGetLocalDocs.mockReturnValue(['[@patternfly/react-guidelines](./guidelines/README.md)']); +describe('listResources', () => { + it('should return a list of resources', async () => { + const resources = await listResources(); + + expect(resources.resources).toBeDefined(); + + const everyResourceSameProperties = resources.resources.every((obj: any) => + Boolean(obj.uri) && + /^patternfly:\/\/docs\//.test(obj.uri) && + Boolean(obj.name) && + Boolean(obj.mimeType) && + Boolean(obj.description)); + + expect(everyResourceSameProperties).toBe(true); + }); +}); + +describe('uriVersionComplete', () => { + it('should attempt to return enumerated PatternFly versions', async () => { + await expect(uriVersionComplete('')).resolves.toEqual(expect.arrayContaining([ + ...DEFAULT_OPTIONS.patternflyOptions.availableSearchVersions + ])); }); +}); +describe('patternFlyDocsIndexResource, callback', () => { it.each([ { description: 'default', args: [] } ])('should return context content, $description', async ({ args }) => { - const [_name, _uri, _config, callback] = patternFlyDocsIndexResource(); - const result = await callback(...args); + const [_name, _uri, _config, callback] = (patternFlyDocsIndexResource as any)(); + const result = await (callback as any)(...args); expect(result.contents).toBeDefined(); expect(Object.keys(result.contents[0])).toEqual(['uri', 'mimeType', 'text']); - expect(result.contents[0].text).toContain('[@patternfly/react-guidelines](./guidelines/README.md)'); + expect(result.contents[0].text).toContain('[AboutModal - Design Guidelines, Accessibility, Examples'); }); }); diff --git a/src/__tests__/resource.patternFlyDocsTemplate.test.ts b/src/__tests__/resource.patternFlyDocsTemplate.test.ts index 4ba9c449..3d13cbb6 100644 --- a/src/__tests__/resource.patternFlyDocsTemplate.test.ts +++ b/src/__tests__/resource.patternFlyDocsTemplate.test.ts @@ -1,22 +1,13 @@ +// import { readFile } from 'node:fs/promises'; import { McpError } from '@modelcontextprotocol/sdk/types.js'; -import { patternFlyDocsTemplateResource } from '../resource.patternFlyDocsTemplate'; -import { processDocsFunction } from '../server.getResources'; -import { searchComponents } from '../tool.searchPatternFlyDocs'; +import { mockReadFile, mockFetch } from '../../jest.setupTests'; +import { + patternFlyDocsTemplateResource, + uriNameComplete, + resourceCallback +} from '../resource.patternFlyDocsTemplate'; import { isPlainObject } from '../server.helpers'; -// Mock dependencies -jest.mock('../server.getResources'); -jest.mock('../tool.searchPatternFlyDocs'); -jest.mock('../server.caching', () => ({ - memo: jest.fn(fn => fn) -})); -jest.mock('../options.context', () => ({ - getOptions: jest.fn(() => ({})) -})); - -const mockProcessDocs = processDocsFunction as jest.MockedFunction; -const mockSearchComponents = searchComponents as jest.MockedFunction; - describe('patternFlyDocsTemplateResource', () => { beforeEach(() => { jest.clearAllMocks(); @@ -34,7 +25,41 @@ describe('patternFlyDocsTemplateResource', () => { }); }); -describe('patternFlyDocsTemplateResource, callback', () => { +describe('uriNameComplete', () => { + it.each([ + { + description: 'with empty string', + value: '', + expected: 10 + }, + { + description: 'with lowercased name', + value: 'button', + expected: 1 + }, + { + description: 'with uppercased name', + value: 'BUTTON', + expected: 1 + }, + { + description: 'with mixed case name', + value: 'bUTTON', + expected: 1 + }, + { + description: 'with empty space and name', + value: ' BUTTON ', + expected: 1 + } + ])('should attempt to return PatternFly component names, $description', async ({ value, expected }) => { + const result = await uriNameComplete(value); + + expect(result.length).toBeGreaterThanOrEqual(expected); + }); +}); + +describe('resourceCallback', () => { beforeEach(() => { jest.clearAllMocks(); }); @@ -42,93 +67,82 @@ describe('patternFlyDocsTemplateResource, callback', () => { it.each([ { description: 'default', - name: 'Button', - urls: ['components/button.md'], - result: 'Button documentation content' - }, - { - description: 'with multiple matched URLs', - name: 'Card', - urls: ['components/card.md', 'components/card-examples.md'], - result: 'Card documentation content' + variables: { + name: 'Button', + version: 'v6' + } }, { - description: 'with trimmed name', - name: ' Table ', - urls: ['components/table.md'], - result: 'Table documentation content' + description: 'with lowercased name', + variables: { + name: 'button', + version: 'v6' + } }, { - description: 'with lower case name', - name: 'button', - urls: ['components/button.md'], - result: 'Button documentation content' + description: 'with local documentation', + variables: { + name: 'chatbot', + version: 'v6' + } } - ])('should parse parameters and return documentation, $description', async ({ name, urls, result: mockResult }) => { - mockSearchComponents.mockReturnValue({ - isSearchWildCardAll: false, - firstExactMatch: undefined, - exactMatches: [{ urls } as any], - searchResults: [] - }); - mockProcessDocs.mockResolvedValue([{ content: mockResult }] as any); + ])('should attempt to return resource content, $description', async ({ variables }) => { + const mockContent = `Mock content for ${variables.name}`; - const [_name, _uri, _config, callback] = patternFlyDocsTemplateResource(); - const uri = new URL('patternfly://docs/Button'); - const variables = { name }; - const result = await callback(uri, variables); + mockReadFile.mockResolvedValue(mockContent); + mockFetch.mockResolvedValue({ + ok: true, + text: () => mockContent + } as any); - expect(mockSearchComponents).toHaveBeenCalledWith(name); - expect(mockProcessDocs).toHaveBeenCalledWith(urls); + const result = await resourceCallback( + { href: `patternfly://docs/${variables.version}/${variables.name}` } as any, + variables + ); expect(result.contents).toBeDefined(); - expect(Object.keys(result.contents[0])).toEqual(['uri', 'mimeType', 'text']); - expect(result.contents[0].text).toContain(mockResult); + expect(Object.keys(result.contents[0] as any)).toEqual(['uri', 'mimeType', 'text']); + expect(result.contents[0]?.text).toMatch(new RegExp(mockContent, 'i')); }); it.each([ { description: 'with missing or undefined name', error: 'Missing required parameter: name must be a string', - variables: {} + variables: { version: 'v6' } }, { description: 'with null name', error: 'Missing required parameter: name must be a string', - variables: { name: null } + variables: { name: null, version: 'v6' } }, { description: 'with empty name', error: 'Missing required parameter: name must be a string', - variables: { name: '' } + variables: { name: '', version: 'v6' } }, { description: 'with non-string name', error: 'Missing required parameter: name must be a string', - variables: { name: 123 } + variables: { name: 123, version: 'v6' } + }, + { + description: 'non-existent name, missing version', + error: 'No documentation found', + variables: { name: 'loremIpsum' } } ])('should handle variable errors, $description', async ({ error, variables }) => { - const [_name, _uri, _config, callback] = patternFlyDocsTemplateResource(); - const uri = new URL('patternfly://docs/test'); + const mockContent = `Mock content for ${variables.name}`; - await expect(callback(uri, variables)).rejects.toThrow(McpError); - await expect(callback(uri, variables)).rejects.toThrow(error); - }); - - it('should handle documentation loading errors', async () => { - mockSearchComponents.mockReturnValue({ - isSearchWildCardAll: false, - firstExactMatch: undefined, - exactMatches: [], - searchResults: [] - }); - mockProcessDocs.mockRejectedValue(new Error('File not found')); + mockReadFile.mockResolvedValue(mockContent); + mockFetch.mockResolvedValue({ + ok: true, + text: () => mockContent + } as any); - const [_name, _uri, _config, handler] = patternFlyDocsTemplateResource(); - const uri = new URL('patternfly://docs/Button'); - const variables = { name: 'Button' }; + const uri = new URL('patternfly://docs/test'); - await expect(handler(uri, variables)).rejects.toThrow(McpError); - await expect(handler(uri, variables)).rejects.toThrow('No documentation found'); + await expect(resourceCallback(uri, variables as any)).rejects.toThrow(McpError); + await expect(resourceCallback(uri, variables as any)).rejects.toThrow(error); }); }); diff --git a/src/__tests__/resource.patternFlySchemasIndex.test.ts b/src/__tests__/resource.patternFlySchemasIndex.test.ts index 504efa0e..0b25c3c4 100644 --- a/src/__tests__/resource.patternFlySchemasIndex.test.ts +++ b/src/__tests__/resource.patternFlySchemasIndex.test.ts @@ -1,11 +1,6 @@ -import { patternFlySchemasIndexResource } from '../resource.patternFlySchemasIndex'; +import { patternFlySchemasIndexResource, listResources } from '../resource.patternFlySchemasIndex'; import { isPlainObject } from '../server.helpers'; -// Mock dependencies -jest.mock('../tool.searchPatternFlyDocs', () => ({ - componentNames: ['Button', 'Card', 'Table'] -})); - describe('patternFlySchemasIndexResource', () => { beforeEach(() => { jest.clearAllMocks(); @@ -23,6 +18,23 @@ describe('patternFlySchemasIndexResource', () => { }); }); +describe('listResources', () => { + it('should return a list of resources', async () => { + const resources = await listResources(); + + expect(resources.resources).toBeDefined(); + + const everyResourceSameProperties = resources.resources.every((obj: any) => + Boolean(obj.uri) && + /^patternfly:\/\/schemas\//.test(obj.uri) && + Boolean(obj.name) && + Boolean(obj.mimeType) && + Boolean(obj.description)); + + expect(everyResourceSameProperties).toBe(true); + }); +}); + describe('patternFlySchemasIndexResource, callback', () => { beforeEach(() => { jest.clearAllMocks(); @@ -39,6 +51,6 @@ describe('patternFlySchemasIndexResource, callback', () => { expect(result.contents).toBeDefined(); expect(Object.keys(result.contents[0])).toEqual(['uri', 'mimeType', 'text']); - expect(result.contents[0].text).toContain('# PatternFly Component Names Index'); + expect(result.contents[0].text).toContain('# PatternFly Component JSON Schemas Index'); }); }); diff --git a/src/__tests__/resource.patternFlySchemasTemplate.test.ts b/src/__tests__/resource.patternFlySchemasTemplate.test.ts index 8af3abaf..ec602620 100644 --- a/src/__tests__/resource.patternFlySchemasTemplate.test.ts +++ b/src/__tests__/resource.patternFlySchemasTemplate.test.ts @@ -1,22 +1,7 @@ import { McpError } from '@modelcontextprotocol/sdk/types.js'; -import { getComponentSchema } from '../tool.patternFlyDocs'; -import { patternFlySchemasTemplateResource } from '../resource.patternFlySchemasTemplate'; -import { searchComponents } from '../tool.searchPatternFlyDocs'; +import { patternFlySchemasTemplateResource, uriNameComplete, resourceCallback } from '../resource.patternFlySchemasTemplate'; import { isPlainObject } from '../server.helpers'; -// Mock dependencies -jest.mock('../tool.searchPatternFlyDocs'); -jest.mock('../tool.patternFlyDocs'); -jest.mock('../server.caching', () => ({ - memo: jest.fn(fn => fn) -})); -jest.mock('../options.context', () => ({ - getOptions: jest.fn(() => ({})) -})); - -const mockGetComponentSchema = getComponentSchema as jest.MockedFunction; -const mockSearchComponents = searchComponents as jest.MockedFunction; - describe('patternFlySchemasTemplateResource', () => { beforeEach(() => { jest.clearAllMocks(); @@ -34,11 +19,71 @@ describe('patternFlySchemasTemplateResource', () => { }); }); -describe('patternFlySchemasTemplateResource, callback', () => { +describe('uriNameComplete', () => { + it.each([ + { + description: 'with empty string', + value: '', + expected: 5 + }, + { + description: 'with lowercased name', + value: 'button', + expected: 1 + }, + { + description: 'with uppercased name', + value: 'BUTTON', + expected: 1 + }, + { + description: 'with mixed case name', + value: 'bUTTON', + expected: 1 + }, + { + description: 'with empty space and name', + value: ' BUTTON ', + expected: 1 + } + ])('should attempt to return PatternFly component names, $description', async ({ value, expected }) => { + const result = await uriNameComplete(value); + + expect(result.length).toBeGreaterThanOrEqual(expected); + }); +}); + +describe('resourceCallback', () => { beforeEach(() => { jest.clearAllMocks(); }); + it.each([ + { + description: 'default', + variables: { + name: 'Button' + } + }, + { + description: 'with lowercased name', + variables: { + name: 'button' + } + } + ])('should attempt to return resource content, $description', async ({ variables }) => { + const mockContent = '$schema'; + + const result = await resourceCallback( + { href: `patternfly://schemas/v6/${variables.name}` } as any, + variables + ); + + expect(result.contents).toBeDefined(); + expect(Object.keys(result.contents[0] as any)).toEqual(['uri', 'mimeType', 'text']); + expect(result.contents[0]?.text).toContain(mockContent); + }); + it.each([ { description: 'with missing or undefined name', @@ -59,46 +104,21 @@ describe('patternFlySchemasTemplateResource, callback', () => { description: 'with non-string name', error: 'Missing required parameter: name must be a string', variables: { name: 123 } + }, + { + description: 'non-existent name', + error: 'not found', + variables: { name: 'loremIpsum' } + }, + { + description: 'found but no schema', + error: 'found but JSON schema not available', + variables: { name: 'table' } } ])('should handle variable errors, $description', async ({ error, variables }) => { - const [_name, _uri, _config, callback] = patternFlySchemasTemplateResource(); - const uri = new URL('patternfly://schemas/test'); - - await expect(callback(uri, variables)).rejects.toThrow(McpError); - await expect(callback(uri, variables)).rejects.toThrow(error); - }); - - it('should handle missing exact match and missing schema errors', async () => { - mockSearchComponents.mockReturnValue({ - isSearchWildCardAll: false, - firstExactMatch: undefined, - exactMatches: [], - searchResults: [] - }); - mockGetComponentSchema.mockReturnValue(undefined as any); - - const [_name, _uri, _config, handler] = patternFlySchemasTemplateResource(); - const uri = new URL('patternfly://schemas/DolorSitAmet'); - const variables = { name: 'DolorSitAmet' }; - - await expect(handler(uri, variables)).rejects.toThrow(McpError); - await expect(handler(uri, variables)).rejects.toThrow('Component "DolorSitAmet" not found'); - }); - - it('should handle exact match but missing schema errors', async () => { - mockSearchComponents.mockReturnValue({ - isSearchWildCardAll: false, - firstExactMatch: undefined, - exactMatches: [{ item: 'Button', urls: [] } as any], - searchResults: [] - }); - mockGetComponentSchema.mockReturnValue(undefined as any); - - const [_name, _uri, _config, handler] = patternFlySchemasTemplateResource(); - const uri = new URL('patternfly://schemas/DolorSitAmet'); - const variables = { name: 'Button' }; + const uri = new URL('patternfly://schemas/v6'); - await expect(handler(uri, variables)).rejects.toThrow(McpError); - await expect(handler(uri, variables)).rejects.toThrow('Component "Button" found'); + await expect(resourceCallback(uri, variables as any)).rejects.toThrow(McpError); + await expect(resourceCallback(uri, variables as any)).rejects.toThrow(error); }); }); diff --git a/src/__tests__/server.test.ts b/src/__tests__/server.test.ts index 9977f9f4..e29f27f6 100644 --- a/src/__tests__/server.test.ts +++ b/src/__tests__/server.test.ts @@ -3,6 +3,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import { runServer } from '../server'; import { log } from '../logger'; import { startHttpTransport, type HttpServerHandle } from '../server.http'; +import { DEFAULT_OPTIONS } from '../options.defaults'; // Mock dependencies jest.mock('@modelcontextprotocol/sdk/server/mcp.js'); @@ -143,12 +144,15 @@ describe('runServer', () => { } ])('should attempt to run server, $description', async ({ options, tools, enableSigint, transportMethod }) => { const settings = { - ...(tools && { tools }), - ...(enableSigint !== undefined && { enableSigint }), + ...tools && { tools }, + ...enableSigint !== undefined && { enableSigint }, allowProcessExit: false // Prevent process.exit in tests }; - const serverInstance = await runServer(options as any, Object.keys(settings).length > 0 ? settings : { allowProcessExit: false }); + const serverInstance = await runServer( + { patternflyOptions: DEFAULT_OPTIONS.patternflyOptions, ...options } as any, + Object.keys(settings).length > 0 ? settings : { allowProcessExit: false } + ); expect(transportMethod).toHaveBeenCalled(); expect(serverInstance.isRunning()).toBe(true); @@ -174,7 +178,10 @@ describe('runServer', () => { options: { isHttp: true } } ])('should allow server to be stopped, $description', async ({ options }) => { - const serverInstance = await runServer({ ...options, name: 'test-server' } as any, { allowProcessExit: false }); + const serverInstance = await runServer( + { patternflyOptions: DEFAULT_OPTIONS.patternflyOptions, ...options, name: 'test-server' } as any, + { allowProcessExit: false } + ); expect(serverInstance.isRunning()).toBe(true); diff --git a/src/__tests__/tool.patternFlyDocs.test.ts b/src/__tests__/tool.patternFlyDocs.test.ts index d73523a8..e4409848 100644 --- a/src/__tests__/tool.patternFlyDocs.test.ts +++ b/src/__tests__/tool.patternFlyDocs.test.ts @@ -34,33 +34,45 @@ describe('usePatternFlyDocsTool, callback', () => { it.each([ { - description: 'default', - value: 'components/button.md', + description: 'single file, mock path', + processedValue: { + path: 'components/button.md', + content: 'single documentation content' + }, urlList: ['components/button.md'] }, { - description: 'multiple files', - value: 'combined docs content', + description: 'multiple files, mock paths', + processedValue: { + path: 'components/button.md', + content: 'combined documentation content' + }, urlList: ['components/button.md', 'components/card.md', 'components/table.md'] }, { description: 'with invalid urlList', - value: 'invalid path', + processedValue: { + path: 'invalid-path', + content: 'Failed to load' + }, urlList: ['invalid-url'] }, { - description: 'with name', - value: 'button content', - name: 'button' + description: 'with name and actual path', + processedValue: { + path: 'documentation:chatbot/README.md', + content: 'chatbot documentation content' + }, + name: 'chatbot' } - ])('should parse parameters, $description', async ({ value, urlList, name }) => { - mockProcessDocs.mockResolvedValue([{ content: value }] as any); + ])('should attempt to parse parameters, $description', async ({ processedValue, urlList, name }) => { + mockProcessDocs.mockResolvedValue([processedValue] as any); const [_name, _schema, callback] = usePatternFlyDocsTool(); const result = await callback({ urlList, name }); expect(mockProcessDocs).toHaveBeenCalledTimes(1); expect(result.content[0].text).toBeDefined(); - expect(result.content[0].text.startsWith('# Documentation from')).toBe(true); + expect(result.content[0].text.split('\n')[0]).toMatchSnapshot(); }); it.each([ diff --git a/src/docs.chart.ts b/src/docs.chart.ts deleted file mode 100644 index d7c14dd5..00000000 --- a/src/docs.chart.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { PF_EXTERNAL_EXAMPLES_CHARTS, PF_EXTERNAL_CHARTS_DESIGN } from './options.defaults'; - -const CHART_DOCS = [ - `[@patternfly/Charts - Colors for Charts - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartTheme/examples/ChartTheme.md)`, - `[@patternfly/Charts - Area Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/area-chart/area-chart.md)`, - `[@patternfly/Charts - Area Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartArea/examples/ChartArea.md)`, - `[@patternfly/Charts - Bar Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/bar-chart/bar-chart.md)`, - `[@patternfly/Charts - Bar Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartBar/examples/ChartBar.md)`, - `[@patternfly/Charts - Box Plot Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartBoxPlot/examples/ChartBoxPlot.md)`, - `[@patternfly/Charts - Bullet Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/bullet-chart/bullet-chart.md)`, - `[@patternfly/Charts - Bullet Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartBullet/examples/ChartBullet.md)`, - `[@patternfly/Charts - Donut Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/donut-chart/donut-chart.md)`, - `[@patternfly/Charts - Donut Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartDonut/examples/ChartDonut.md)`, - `[@patternfly/Charts - Donut Utilization Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/donut-utilization-chart/donut-utilization-chart.md)`, - `[@patternfly/Charts - Donut Utilization Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartDonutUtilization/examples/ChartDonutUtilization.md)`, - `[@patternfly/Charts - Line Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/line-chart/line-chart.md)`, - `[@patternfly/Charts - Line Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartLine/examples/ChartLine.md)`, - `[@patternfly/Charts - Pie Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/pie-chart/pie-chart.md)`, - `[@patternfly/Charts - Pie Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartPie/examples/ChartPie.md)`, - `[@patternfly/Charts - Scatter Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/scatter-chart/scatter-chart.md)`, - `[@patternfly/Charts - Scatter Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartScatter/examples/ChartScatter.md)`, - `[@patternfly/Charts - Sparkline Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/sparkline-chart/sparkline-chart.md)`, - `[@patternfly/Charts - Sparkline Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/Sparkline/examples/sparkline.md)`, - `[@patternfly/Charts - Stack Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/stacked-chart/stacked-chart.md)`, - `[@patternfly/Charts - Stack Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartStack/examples/ChartStack.md)`, - `[@patternfly/Charts - Threshold Chart - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/threshold-chart/threshold-chart.md)`, - `[@patternfly/Charts - Threshold Chart - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartThreshold/examples/ChartThreshold.md)`, - `[@patternfly/Charts - Legend - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/legend-chart/legend-chart.md)`, - `[@patternfly/Charts - Legend - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartLegend/examples/ChartLegend.md)`, - `[@patternfly/Charts - Tooltip - Design Guidelines](${PF_EXTERNAL_CHARTS_DESIGN}/tooltip-chart/tooltip-chart.md)`, - `[@patternfly/Charts - Tooltip - Examples](${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartTooltip/examples/ChartTooltip.md)` -]; - -export { CHART_DOCS }; diff --git a/src/docs.component.ts b/src/docs.component.ts deleted file mode 100644 index ca4f93f5..00000000 --- a/src/docs.component.ts +++ /dev/null @@ -1,264 +0,0 @@ -import { PF_EXTERNAL_ACCESSIBILITY, PF_EXTERNAL_DESIGN_COMPONENTS, PF_EXTERNAL_EXAMPLES_REACT_CORE, PF_EXTERNAL_EXAMPLES_TABLE } from './options.defaults'; - -const COMPONENT_DOCS = [ - `[@patternfly/AboutModal - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/about-modal/about-modal.md)`, - `[@patternfly/AboutModal - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/about-modal/about-modal.md)`, - `[@patternfly/AboutModal - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/AboutModal/examples/AboutModal.md)`, - `[@patternfly/Accordion - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/accordion/accordion.md)`, - `[@patternfly/Accordion - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/accordion/accordion.md)`, - `[@patternfly/Accordion - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Accordion/examples/Accordion.md)`, - `[@patternfly/ActionList - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/action-list/action-list.md)`, - `[@patternfly/ActionList - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/action-list/action-list.md)`, - `[@patternfly/ActionList - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/ActionList/examples/ActionList.md)`, - `[@patternfly/Alert - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/alert/alert.md)`, - `[@patternfly/Alert - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/alert/alert.md)`, - `[@patternfly/Alert - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Alert/examples/Alert.md)`, - `[@patternfly/ApplicationLauncher - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/application-launcher/application-launcher.md)`, - `[@patternfly/ApplicationLauncher - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/application-launcher/application-launcher.md)`, - // `[@patternfly/ApplicationLauncher - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/ApplicationLauncher/examples/ApplicationLauncher.md)`, - `[@patternfly/Avatar - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/avatar/avatar.md)`, - `[@patternfly/Avatar - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/avatar/avatar.md)`, - `[@patternfly/Avatar - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Avatar/examples/Avatar.md)`, - `[@patternfly/BackToTop - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/back-to-top/back-to-top.md)`, - // `[@patternfly/BackToTop - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/back-to-top/back-to-top.md)`, - `[@patternfly/BackToTop - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/BackToTop/examples/BackToTop.md)`, - `[@patternfly/Backdrop - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/backdrop/backdrop.md)`, - `[@patternfly/Backdrop - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/backdrop/backdrop.md)`, - `[@patternfly/Backdrop - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Backdrop/examples/Backdrop.md)`, - `[@patternfly/BackgroundImage - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/background-image/background-image.md)`, - `[@patternfly/BackgroundImage - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/background-image/background-image.md)`, - `[@patternfly/BackgroundImage - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/BackgroundImage/examples/BackgroundImage.md)`, - `[@patternfly/Badge - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/badge/badge.md)`, - `[@patternfly/Badge - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/badge/badge.md)`, - `[@patternfly/Badge - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Badge/examples/Badge.md)`, - `[@patternfly/Banner - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/banner/banner.md)`, - `[@patternfly/Banner - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/banner/banner.md)`, - `[@patternfly/Banner - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Banner/examples/Banner.md)`, - `[@patternfly/Brand - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/brand/brand.md)`, - `[@patternfly/Brand - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/brand/brand.md)`, - `[@patternfly/Brand - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Brand/examples/Brand.md)`, - `[@patternfly/Breadcrumb - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/breadcrumb/breadcrumb.md)`, - `[@patternfly/Breadcrumb - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/breadcrumb/breadcrumb.md)`, - `[@patternfly/Breadcrumb - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Breadcrumb/examples/Breadcrumb.md)`, - `[@patternfly/Button - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/button/button.md)`, - `[@patternfly/Button - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/button/button.md)`, - `[@patternfly/Button - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Button/examples/Button.md)`, - `[@patternfly/CalendarMonth - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/calendar-month/calendar-month.md)`, - `[@patternfly/CalendarMonth - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/calendar-month/calendar-month.md)`, - `[@patternfly/CalendarMonth - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/CalendarMonth/examples/CalendarMonth.md)`, - `[@patternfly/Card - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/card/card.md)`, - `[@patternfly/Card - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/card/card.md)`, - `[@patternfly/Card - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Card/examples/Card.md)`, - `[@patternfly/Checkbox - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/checkbox/checkbox.md)`, - `[@patternfly/Checkbox - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/checkbox/checkbox.md)`, - `[@patternfly/Checkbox - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Checkbox/examples/Checkbox.md)`, - `[@patternfly/ChipDeprecated - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/chip/chip.md)`, - `[@patternfly/ChipDeprecated - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/chip/chip.md)`, - // `[@patternfly/ChipDeprecated - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/ChipDeprecated/examples/ChipDeprecated.md)`, - `[@patternfly/ClipboardCopy - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/clipboard-copy/clipboard-copy.md)`, - `[@patternfly/ClipboardCopy - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/clipboard-copy/clipboard-copy.md)`, - `[@patternfly/ClipboardCopy - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/ClipboardCopy/examples/ClipboardCopy.md)`, - `[@patternfly/CodeBlock - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/code-block/code-block.md)`, - // `[@patternfly/CodeBlock - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/code-block/code-block.md)`, - `[@patternfly/CodeBlock - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/CodeBlock/examples/CodeBlock.md)`, - `[@patternfly/CodeEditor - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/code-editor/code-editor.md)`, - `[@patternfly/CodeEditor - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/code-editor/code-editor.md)`, - // `[@patternfly/CodeEditor - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/CodeEditor/examples/CodeEditor.md)`, - `[@patternfly/Content - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/content/content.md)`, - // `[@patternfly/Content - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/content/content.md)`, - `[@patternfly/Content - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Content/examples/Content.md)`, - `[@patternfly/DataList - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/data-list/data-list.md)`, - // `[@patternfly/DataList - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/data-list/data-list.md)`, - `[@patternfly/DataList - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/DataList/examples/DataList.md)`, - `[@patternfly/DatePicker - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/date-picker/date-picker.md)`, - // `[@patternfly/DatePicker - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/date-picker/date-picker.md)`, - `[@patternfly/DatePicker - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/DatePicker/examples/DatePicker.md)`, - // `[@patternfly/DateTimePicker - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/date-time-picker/date-time-picker.md)`, - // `[@patternfly/DateTimePicker - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/date-time-picker/date-time-picker.md)`, - // `[@patternfly/DateTimePicker - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/DateTimePicker/examples/DateTimePicker.md)`, - `[@patternfly/DescriptionList - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/description-list/description-list.md)`, - // `[@patternfly/DescriptionList - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/description-list/description-list.md)`, - `[@patternfly/DescriptionList - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/DescriptionList/examples/DescriptionList.md)`, - `[@patternfly/Divider - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/divider/divider.md)`, - // `[@patternfly/Divider - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/divider/divider.md)`, - `[@patternfly/Divider - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Divider/examples/Divider.md)`, - `[@patternfly/DragAndDrop - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/drag-and-drop/drag.md)`, - // `[@patternfly/DragAndDrop - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/drag-and-drop/drag-and-drop.md)`, - // `[@patternfly/DragAndDrop - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/DragAndDrop/examples/DragAndDrop.md)`, - `[@patternfly/Drawer - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/drawer/drawer.md)`, - // `[@patternfly/Drawer - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/drawer/drawer.md)`, - `[@patternfly/Drawer - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Drawer/examples/Drawer.md)`, - `[@patternfly/Dropdown - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/dropdown/dropdown.md)`, - // `[@patternfly/Dropdown - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/dropdown/dropdown.md)`, - `[@patternfly/Dropdown - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Dropdown/examples/Dropdown.md)`, - `[@patternfly/DualListSelector - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/dual-list-selector/dual-list-selector.md)`, - // `[@patternfly/DualListSelector - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/dual-list-selector/dual-list-selector.md)`, - `[@patternfly/DualListSelector - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/DualListSelector/examples/DualListSelector.md)`, - `[@patternfly/EmptyState - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/empty-state/empty-state.md)`, - // `[@patternfly/EmptyState - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/empty-state/empty-state.md)`, - `[@patternfly/EmptyState - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/EmptyState/examples/EmptyState.md)`, - `[@patternfly/ExpandableSection - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/expandable-section/expandable-section.md)`, - `[@patternfly/ExpandableSection - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/expandable-section/expandable-section.md)`, - `[@patternfly/ExpandableSection - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/ExpandableSection/examples/ExpandableSection.md)`, - `[@patternfly/FileUpload - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/file-upload/file-upload.md)`, - // `[@patternfly/FileUpload - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/file-upload/file-upload.md)`, - `[@patternfly/FileUpload - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/FileUpload/examples/FileUpload.md)`, - `[@patternfly/Form - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/form/forms.md)`, - // `[@patternfly/Form - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/form/form.md)`, - `[@patternfly/Form - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Form/examples/Form.md)`, - `[@patternfly/FormControl - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/form-control/form-control.md)`, - // `[@patternfly/FormControl - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/form-control/form-control.md)`, - // `[@patternfly/FormControl - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/FormControl/examples/FormControl.md)`, - `[@patternfly/FormSelect - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/form-select/form-select.md)`, - // `[@patternfly/FormSelect - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/form-select/form-select.md)`, - `[@patternfly/FormSelect - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/FormSelect/examples/FormSelect.md)`, - `[@patternfly/HelperText - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/helper-text/helper-text.md)`, - `[@patternfly/HelperText - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/helper-text/helper-text.md)`, - `[@patternfly/HelperText - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/HelperText/examples/HelperText.md)`, - `[@patternfly/Hint - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/hint/hint.md)`, - // `[@patternfly/Hint - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/hint/hint.md)`, - `[@patternfly/Hint - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Hint/examples/Hint.md)`, - // `[@patternfly/Icon - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/icon/icon.md)`, - // `[@patternfly/Icon - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/icon/icon.md)`, - `[@patternfly/Icon - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Icon/examples/Icon.md)`, - `[@patternfly/InlineEdit - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/inline-edit/inline-edit.md)`, - // `[@patternfly/InlineEdit - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/inline-edit/inline-edit.md)`, - // `[@patternfly/InlineEdit - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/InlineEdit/examples/InlineEdit.md)`, - `[@patternfly/InputGroup - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/input-group/input-group.md)`, - // `[@patternfly/InputGroup - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/input-group/input-group.md)`, - `[@patternfly/InputGroup - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/InputGroup/examples/InputGroup.md)`, - `[@patternfly/JumpLinks - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/jump-link/jump-link.md)`, - `[@patternfly/JumpLinks - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/jump-links/jump-links.md)`, - `[@patternfly/JumpLinks - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/JumpLinks/examples/JumpLinks.md)`, - `[@patternfly/Label - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/label/label.md)`, - `[@patternfly/Label - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/label/label.md)`, - `[@patternfly/Label - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Label/examples/Label.md)`, - `[@patternfly/List - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/list/list.md)`, - // `[@patternfly/List - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/list/list.md)`, - `[@patternfly/List - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/List/examples/List.md)`, - `[@patternfly/LoginPage - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/login-page/login-page.md)`, - // `[@patternfly/LoginPage - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/login-page/login-page.md)`, - `[@patternfly/LoginPage - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/LoginPage/examples/LoginPage.md)`, - `[@patternfly/Masthead - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/masthead/masthead.md)`, - // `[@patternfly/Masthead - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/masthead/masthead.md)`, - `[@patternfly/Masthead - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Masthead/examples/Masthead.md)`, - `[@patternfly/Menu - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/menu/menu.md)`, - `[@patternfly/Menu - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/menu/menu.md)`, - `[@patternfly/Menu - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Menu/examples/Menu.md)`, - `[@patternfly/MenuToggle - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/menu-toggle/menu-toggle.md)`, - `[@patternfly/MenuToggle - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/menu-toggle/menu-toggle.md)`, - `[@patternfly/MenuToggle - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/MenuToggle/examples/MenuToggle.md)`, - `[@patternfly/Modal - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/modal/modal.md)`, - `[@patternfly/Modal - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/modal/modal.md)`, - `[@patternfly/Modal - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Modal/examples/Modal.md)`, - `[@patternfly/Navigation - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/navigation/navigation.md)`, - `[@patternfly/Navigation - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/navigation/navigation.md)`, - // `[@patternfly/Navigation - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Navigation/examples/Navigation.md)`, - `[@patternfly/NotificationBadge - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/notification-badge/notification-badge.md)`, - // `[@patternfly/NotificationBadge - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/notification-badge/notification-badge.md)`, - `[@patternfly/NotificationBadge - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/NotificationBadge/examples/NotificationBadge.md)`, - `[@patternfly/NotificationDrawer - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/notification-drawer/notification-drawer.md)`, - // `[@patternfly/NotificationDrawer - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/notification-drawer/notification-drawer.md)`, - `[@patternfly/NotificationDrawer - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/NotificationDrawer/examples/NotificationDrawer.md)`, - `[@patternfly/NumberInput - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/number%20input/number-input.md)`, - // `[@patternfly/NumberInput - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/number-input/number-input.md)`, - `[@patternfly/NumberInput - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/NumberInput/examples/NumberInput.md)`, - `[@patternfly/OverflowMenu - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/overflow-menu/overflow-menu.md)`, - // `[@patternfly/OverflowMenu - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/overflow-menu/overflow-menu.md)`, - `[@patternfly/OverflowMenu - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/OverflowMenu/examples/OverflowMenu.md)`, - `[@patternfly/Page - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/page/page.md)`, - `[@patternfly/Page - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/page/page.md)`, - `[@patternfly/Page - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Page/examples/Page.md)`, - `[@patternfly/Pagination - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/pagination/pagination.md)`, - // `[@patternfly/Pagination - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/pagination/pagination.md)`, - `[@patternfly/Pagination - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Pagination/examples/Pagination.md)`, - `[@patternfly/Panel - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/panel/panel.md)`, - // `[@patternfly/Panel - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/panel/panel.md)`, - `[@patternfly/Panel - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Panel/examples/Panel.md)`, - `[@patternfly/Popover - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/popover/popover.md)`, - // `[@patternfly/Popover - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/popover/popover.md)`, - `[@patternfly/Popover - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Popover/examples/Popover.md)`, - `[@patternfly/Progress - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/progress/progress.md)`, - `[@patternfly/Progress - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/progress/progress.md)`, - `[@patternfly/Progress - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Progress/examples/Progress.md)`, - `[@patternfly/ProgressStepper - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/progress-stepper/progress-stepper.md)`, - `[@patternfly/ProgressStepper - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/progress-stepper/progress-stepper.md)`, - `[@patternfly/ProgressStepper - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/ProgressStepper/examples/ProgressStepper.md)`, - `[@patternfly/Radio - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/radio/radio.md)`, - `[@patternfly/Radio - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/radio/radio.md)`, - `[@patternfly/Radio - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Radio/examples/Radio.md)`, - `[@patternfly/SearchInput - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/search-input/search-input.md)`, - // `[@patternfly/SearchInput - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/search-input/search-input.md)`, - `[@patternfly/SearchInput - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/SearchInput/examples/SearchInput.md)`, - `[@patternfly/Select - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/select/select.md)`, - // `[@patternfly/Select - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/select/select.md)`, - `[@patternfly/Select - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Select/examples/Select.md)`, - `[@patternfly/Sidebar - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/sidebar/sidebar.md)`, - `[@patternfly/Sidebar - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/sidebar/sidebar.md)`, - `[@patternfly/Sidebar - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Sidebar/examples/Sidebar.md)`, - `[@patternfly/SimpleList - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/simple-list/simple-list.md)`, - // `[@patternfly/SimpleList - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/simple-list/simple-list.md)`, - `[@patternfly/SimpleList - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/SimpleList/examples/SimpleList.md)`, - `[@patternfly/Skeleton - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/skeleton/skeleton.md)`, - `[@patternfly/Skeleton - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/skeleton/skeleton.md)`, - `[@patternfly/Skeleton - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Skeleton/examples/Skeleton.md)`, - `[@patternfly/SkipToContent - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/skip-to-content/skip-to-content.md)`, - `[@patternfly/SkipToContent - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/skip-to-content/skip-to-content.md)`, - `[@patternfly/SkipToContent - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/SkipToContent/examples/SkipToContent.md)`, - `[@patternfly/Slider - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/slider/slider.md)`, - // `[@patternfly/Slider - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/slider/slider.md)`, - `[@patternfly/Slider - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Slider/examples/Slider.md)`, - `[@patternfly/Spinner - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/spinner/spinner.md)`, - // `[@patternfly/Spinner - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/spinner/spinner.md)`, - `[@patternfly/Spinner - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Spinner/examples/Spinner.md)`, - `[@patternfly/Switch - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/switch/switch.md)`, - `[@patternfly/Switch - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/switch/switch.md)`, - `[@patternfly/Switch - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Switch/examples/Switch.md)`, - `[@patternfly/Table - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/table/table.md)`, - // `[@patternfly/Table - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/table/table.md)`, - `[@patternfly/Table - Examples](${PF_EXTERNAL_EXAMPLES_TABLE}/Table/examples/Table.md)`, - `[@patternfly/Tabs - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/tabs/tabs.md)`, - `[@patternfly/Tabs - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/tabs/tabs.md)`, - `[@patternfly/Tabs - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Tabs/examples/Tabs.md)`, - `[@patternfly/TextArea - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/text-area/text-area.md)`, - // `[@patternfly/TextArea - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/text-area/text-area.md)`, - `[@patternfly/TextArea - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/TextArea/examples/TextArea.md)`, - `[@patternfly/TextInput - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/text-input/text-input.md)`, - // `[@patternfly/TextInput - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/text-input/text-input.md)`, - `[@patternfly/TextInput - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/TextInput/examples/TextInput.md)`, - `[@patternfly/TextInputGroup - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/text-input-group/text-input-group.md)`, - // `[@patternfly/TextInputGroup - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/text-input-group/text-input-group.md)`, - `[@patternfly/TextInputGroup - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/TextInputGroup/examples/TextInputGroup.md)`, - `[@patternfly/TileDeprecated - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/tile/tile.md)`, - // `[@patternfly/TileDeprecated - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/tile-deprecated/tile-deprecated.md)`, - // `[@patternfly/TileDeprecated - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/TileDeprecated/examples/TileDeprecated.md)`, - `[@patternfly/TimePicker - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/time%20picker/time-picker.md)`, - // `[@patternfly/TimePicker - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/time-picker/time-picker.md)`, - `[@patternfly/TimePicker - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/TimePicker/examples/TimePicker.md)`, - `[@patternfly/Timestamp - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/timestamp/timestamp.md)`, - // `[@patternfly/Timestamp - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/timestamp/timestamp.md)`, - `[@patternfly/Timestamp - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Timestamp/examples/Timestamp.md)`, - `[@patternfly/Title - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/title/title.md)`, - `[@patternfly/Title - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/title/title.md)`, - `[@patternfly/Title - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Title/examples/Title.md)`, - `[@patternfly/ToggleGroup - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/toggle-group/toggle-group.md)`, - // `[@patternfly/ToggleGroup - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/toggle-group/toggle-group.md)`, - `[@patternfly/ToggleGroup - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/ToggleGroup/examples/ToggleGroup.md)`, - `[@patternfly/Toolbar - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/toolbar/toolbar.md)`, - // `[@patternfly/Toolbar - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/toolbar/toolbar.md)`, - `[@patternfly/Toolbar - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Toolbar/examples/Toolbar.md)`, - `[@patternfly/Tooltip - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/tooltip/tooltip.md)`, - `[@patternfly/Tooltip - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/tooltip/tooltip.md)`, - `[@patternfly/Tooltip - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Tooltip/examples/Tooltip.md)`, - `[@patternfly/TreeView - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/tree-view/tree-view.md)`, - `[@patternfly/TreeView - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/tree-view/tree-view.md)`, - `[@patternfly/TreeView - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/TreeView/examples/TreeView.md)`, - `[@patternfly/Truncate - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/truncate/truncate.md)`, - // `[@patternfly/Truncate - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/truncate/truncate.md)`, - `[@patternfly/Truncate - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Truncate/examples/Truncate.md)`, - `[@patternfly/Wizard - Design Guidelines](${PF_EXTERNAL_DESIGN_COMPONENTS}/wizard/wizard.md)`, - // `[@patternfly/Wizard - Accessibility](${PF_EXTERNAL_ACCESSIBILITY}/wizard/wizard.md)`, - `[@patternfly/Wizard - Examples](${PF_EXTERNAL_EXAMPLES_REACT_CORE}/Wizard/examples/Wizard.md)` -]; - -export { COMPONENT_DOCS }; diff --git a/src/docs.layout.ts b/src/docs.layout.ts deleted file mode 100644 index 68c409a1..00000000 --- a/src/docs.layout.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { PF_EXTERNAL_DESIGN_LAYOUTS, PF_EXTERNAL_EXAMPLES_LAYOUTS } from './options.defaults'; - -const LAYOUT_DOCS = [ - `[@patternfly/Bullseye - Design Guidelines](${PF_EXTERNAL_DESIGN_LAYOUTS}/bullseye.md)`, - `[@patternfly/Bullseye - Examples](${PF_EXTERNAL_EXAMPLES_LAYOUTS}/Bullseye/examples/Bullseye.md)`, - `[@patternfly/Flex - Design Guidelines](${PF_EXTERNAL_DESIGN_LAYOUTS}/flex.md)`, - `[@patternfly/Flex - Examples](${PF_EXTERNAL_EXAMPLES_LAYOUTS}/Flex/examples/Flex.md)`, - `[@patternfly/Gallery - Design Guidelines](${PF_EXTERNAL_DESIGN_LAYOUTS}/gallery.md)`, - `[@patternfly/Gallery - Examples](${PF_EXTERNAL_EXAMPLES_LAYOUTS}/Gallery/examples/Gallery.md)`, - `[@patternfly/Grid - Design Guidelines](${PF_EXTERNAL_DESIGN_LAYOUTS}/grid.md)`, - `[@patternfly/Grid - Examples](${PF_EXTERNAL_EXAMPLES_LAYOUTS}/Grid/examples/Grid.md)`, - `[@patternfly/Level - Design Guidelines](${PF_EXTERNAL_DESIGN_LAYOUTS}/level.md)`, - `[@patternfly/Level - Examples](${PF_EXTERNAL_EXAMPLES_LAYOUTS}/Level/examples/Level.md)`, - `[@patternfly/Split - Design Guidelines](${PF_EXTERNAL_DESIGN_LAYOUTS}/split.md)`, - `[@patternfly/Split - Examples](${PF_EXTERNAL_EXAMPLES_LAYOUTS}/Split/examples/Split.md)`, - `[@patternfly/Stack - Design Guidelines](${PF_EXTERNAL_DESIGN_LAYOUTS}/stack.md)`, - `[@patternfly/Stack - Examples](${PF_EXTERNAL_EXAMPLES_LAYOUTS}/Stack/examples/Stack.md)` -]; - -export { LAYOUT_DOCS }; diff --git a/src/docs.local.ts b/src/docs.local.ts deleted file mode 100644 index 0afcca13..00000000 --- a/src/docs.local.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { join } from 'node:path'; -import { getOptions } from './options.context'; - -/** - * Get local documentation paths - * - * @param options - */ -const getLocalDocs = (options = getOptions()) => [ - `[@patternfly/react-charts](${join(options.docsPath, 'charts', 'README.md')})`, - `[@patternfly/react-chatbot](${join(options.docsPath, 'chatbot', 'README.md')})`, - `[@patternfly/react-component-groups](${join(options.docsPath, 'component-groups', 'README.md')})`, - `[@patternfly/react-components](${join(options.docsPath, 'components', 'README.md')})`, - `[@patternfly/react-guidelines](${join(options.docsPath, 'guidelines', 'README.md')})`, - `[@patternfly/react-resources](${join(options.docsPath, 'resources', 'README.md')})`, - `[@patternfly/react-setup](${join(options.docsPath, 'setup', 'README.md')})`, - `[@patternfly/react-troubleshooting](${join(options.docsPath, 'troubleshooting', 'README.md')})` -]; - -export { getLocalDocs }; diff --git a/src/options.defaults.ts b/src/options.defaults.ts index cc44bb1a..093646a4 100644 --- a/src/options.defaults.ts +++ b/src/options.defaults.ts @@ -27,15 +27,6 @@ import { type ToolModule } from './server.toolsUser'; * @property name - Name of the package. * @property nodeVersion - Node.js major version. * @property {PatternFlyOptions} patternflyOptions - PatternFly-specific options. - * @property pfExternal - PatternFly external docs URL. - * @property pfExternalDesignComponents - PatternFly design guidelines' components' URL. - * @property pfExternalExamplesComponents - PatternFly examples' core components' URL. - * @property pfExternalExamplesLayouts - PatternFly examples' core layouts' URL. - * @property pfExternalExamplesCharts - PatternFly examples' charts' components' URL. - * @property pfExternalExamplesTable - PatternFly examples' table components' URL. - * @property pfExternalChartsDesign - PatternFly charts' design guidelines URL. - * @property pfExternalDesignLayouts - PatternFly design guidelines' layouts' URL. - * @property pfExternalAccessibility - PatternFly accessibility URL. * @property pluginIsolation - Isolation preset for external plugins. * @property {PluginHostOptions} pluginHost - Plugin host options. * @property repoName - Name of the repository. @@ -67,15 +58,6 @@ interface DefaultOptions { patternflyOptions: PatternFlyOptions; pluginIsolation: 'none' | 'strict'; pluginHost: PluginHostOptions; - pfExternal: string; - pfExternalDesignComponents: string; - pfExternalExamplesComponents: string; - pfExternalExamplesLayouts: string; - pfExternalExamplesCharts: string; - pfExternalExamplesTable: string; - pfExternalChartsDesign: string; - pfExternalDesignLayouts: string; - pfExternalAccessibility: string; repoName: string | undefined; resourceMemoOptions: Partial; resourceModules: unknown | unknown[]; @@ -384,68 +366,6 @@ const URL_REGEX = /^(https?:)\/\//i; */ const MODE_LEVELS: DefaultOptions['mode'][] = ['cli', 'programmatic', 'test']; -const PF_EXTERNAL_EXAMPLES_VERSION = 'v6.4.0'; - -/** - * PatternFly examples URL - */ -const PF_EXTERNAL_EXAMPLES = `https://raw.githubusercontent.com/patternfly/patternfly-react/refs/tags/${PF_EXTERNAL_EXAMPLES_VERSION}/packages`; - -/** - * PatternFly examples' core components' URL. - */ -const PF_EXTERNAL_EXAMPLES_REACT_CORE = `${PF_EXTERNAL_EXAMPLES}/react-core/src/components`; - -/** - * PatternFly examples' core layouts' URL. - */ -const PF_EXTERNAL_EXAMPLES_LAYOUTS = `${PF_EXTERNAL_EXAMPLES}/react-core/src/layouts`; - -/** - * PatternFly examples' table components' URL. - */ -const PF_EXTERNAL_EXAMPLES_TABLE = `${PF_EXTERNAL_EXAMPLES}/react-table/src/components`; - -/** - * PatternFly charts' components' URL - */ -const PF_EXTERNAL_EXAMPLES_CHARTS = `${PF_EXTERNAL_EXAMPLES}/react-charts/src/victory/components`; - -/** - * PatternFly docs version to use, commit hash. Tags don't exist, but branches for older versions do. - * - * @see @patternfly/documentation-framework@6.30.0 - */ -const PF_EXTERNAL_VERSION = 'fb05713aba75998b5ecf5299ee3c1a259119bd74'; - -/** - * PatternFly docs root URL - */ -const PF_EXTERNAL = `https://raw.githubusercontent.com/patternfly/patternfly-org/${PF_EXTERNAL_VERSION}/packages/documentation-site/patternfly-docs/content`; - -/** - * PatternFly design guidelines' components' URL - * Updated 2025-11-24: Moved from design-guidelines/components to components - */ -const PF_EXTERNAL_DESIGN_COMPONENTS = `${PF_EXTERNAL}/design-guidelines/components`; - -/** - * PatternFly design guidelines' layouts' URL - * Updated 2025-11-24: Moved from design-guidelines/layouts to foundations-and-styles/layouts - */ -const PF_EXTERNAL_DESIGN_LAYOUTS = `${PF_EXTERNAL}/design-guidelines/layouts`; - -/** - * PatternFly accessibility URL - * Updated 2025-11-24: Moved from accessibility to components/accessibility - */ -const PF_EXTERNAL_ACCESSIBILITY = `${PF_EXTERNAL}/accessibility`; - -/** - * PatternFly charts' design guidelines URL - */ -const PF_EXTERNAL_CHARTS_DESIGN = `${PF_EXTERNAL}/design-guidelines/charts`; - /** * Get the current Node.js major version. * @@ -487,15 +407,6 @@ const DEFAULT_OPTIONS: DefaultOptions = { name: packageJson.name, nodeVersion: (process.env.NODE_ENV === 'local' && 22) || getNodeMajorVersion(), patternflyOptions: PATTERNFLY_OPTIONS, - pfExternal: PF_EXTERNAL, - pfExternalDesignComponents: PF_EXTERNAL_DESIGN_COMPONENTS, - pfExternalExamplesComponents: PF_EXTERNAL_EXAMPLES_REACT_CORE, - pfExternalExamplesLayouts: PF_EXTERNAL_EXAMPLES_LAYOUTS, - pfExternalExamplesCharts: PF_EXTERNAL_EXAMPLES_CHARTS, - pfExternalExamplesTable: PF_EXTERNAL_EXAMPLES_TABLE, - pfExternalChartsDesign: PF_EXTERNAL_CHARTS_DESIGN, - pfExternalDesignLayouts: PF_EXTERNAL_DESIGN_LAYOUTS, - pfExternalAccessibility: PF_EXTERNAL_ACCESSIBILITY, pluginIsolation: 'strict', pluginHost: PLUGIN_HOST_OPTIONS, resourceMemoOptions: RESOURCE_MEMO_OPTIONS, @@ -514,18 +425,6 @@ export { LOG_BASENAME, DEFAULT_OPTIONS, MODE_LEVELS, - PF_EXTERNAL, - PF_EXTERNAL_VERSION, - PF_EXTERNAL_EXAMPLES, - PF_EXTERNAL_EXAMPLES_CHARTS, - PF_EXTERNAL_EXAMPLES_REACT_CORE, - PF_EXTERNAL_EXAMPLES_LAYOUTS, - PF_EXTERNAL_EXAMPLES_TABLE, - PF_EXTERNAL_EXAMPLES_VERSION, - PF_EXTERNAL_CHARTS_DESIGN, - PF_EXTERNAL_DESIGN_COMPONENTS, - PF_EXTERNAL_DESIGN_LAYOUTS, - PF_EXTERNAL_ACCESSIBILITY, getNodeMajorVersion, type DefaultOptions, type DefaultOptionsOverrides, diff --git a/src/resource.patternFlyComponentsIndex.ts b/src/resource.patternFlyComponentsIndex.ts new file mode 100644 index 00000000..dadaf4fa --- /dev/null +++ b/src/resource.patternFlyComponentsIndex.ts @@ -0,0 +1,145 @@ +import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { type McpResource } from './server'; +import { memo } from './server.caching'; +import { stringJoin } from './server.helpers'; +import { getOptions, runWithOptions } from './options.context'; +import { + getPatternFlyMcpResources, + getPatternFlyReactComponentNames +} from './patternFly.getResources'; +import { uriVersionComplete, type PatterFlyListResourceResult } from './resource.patternFlyDocsIndex'; +import { normalizeEnumeratedPatternFlyVersion } from './patternFly.helpers'; + +/** + * Name of the resource. + */ +const NAME = 'patternfly-components-index'; + +/** + * URI template for the resource. + */ +const URI_TEMPLATE = 'patternfly://components/index{?version,section,category}'; + +/** + * Resource configuration. + */ +const CONFIG = { + title: 'PatternFly Components Index', + description: 'A list of all PatternFly component names available for documentation retrieval', + mimeType: 'text/markdown' +}; + +/** + * List resources callback for the URI template. + * + * @returns {Promise} The list of available resources. + */ +const listResources = async () => { + const { byVersionComponentNames, resources: docsResources } = await getPatternFlyMcpResources.memo(); + const { componentNamesWithSchemasMap } = await getPatternFlyReactComponentNames.memo(); + const resources: PatterFlyListResourceResult[] = []; + + Object.entries(byVersionComponentNames).sort(([a], [b]) => b.localeCompare(a)).forEach(([version, componentNames]) => { + const versionResource: PatterFlyListResourceResult[] = []; + + componentNames.forEach(componentName => { + const displayName = componentNamesWithSchemasMap[componentName]; + const isSchemasAvailable = docsResources.get(componentName)?.versions?.[version]?.isSchemasAvailable ?? false; + + versionResource.push({ + uri: `patternfly://docs/${version}/${componentName}`, + mimeType: 'text/markdown', + name: `${displayName} (${version})`, + description: `Component documentation for PatternFly version "${version}" of "${displayName}.${isSchemasAvailable ? ' (JSON Schema available)' : ''}"` + }); + }); + + resources.push(...versionResource); + }); + + return { + resources + }; +}; + +/** + * Memoized version of listResources. + */ +listResources.memo = memo(listResources); + +/** + * Resource callback for the documentation index. + * + * @param uri - URI of the resource. + * @param variables - Variables for the resource. + * @returns The resource contents. + */ +const resourceCallback = async (uri: URL, variables: Record) => { + const { version } = variables || {}; + let updatedVersion = await normalizeEnumeratedPatternFlyVersion.memo(version); + const { latestVersion, byVersion, resources } = await getPatternFlyMcpResources.memo(); + + if (!updatedVersion) { + updatedVersion = latestVersion; + } + + const entries = byVersion[updatedVersion] || []; + + // Group by URI + const groupedByUri = new Map(); + + entries.forEach(entry => { + const entryName = entry.name.toLowerCase(); + const resource = resources.get(entryName)?.versions[updatedVersion]; + + if (resource?.uri) { + groupedByUri.set(resource.uri, { name: entry.name, version: entry.version }); + } + }); + + // Generate the consolidated list + const docsIndex = Array.from(groupedByUri.entries()) + .sort(([_aUri, aData], [_bUri, bData]) => aData.name.localeCompare(bData.name)) + .map(([uri, data], index) => `${index + 1}. [${data.name} (${data.version})](${uri})`); + + return { + contents: [{ + uri: 'patternfly://schemas/index', + mimeType: 'text/markdown', + text: stringJoin.newline( + `# PatternFly Component Names Index for "${updatedVersion}"`, + '', + '', + ...docsIndex || [] + ) + }] + }; +}; + +/** + * Resource creator for the component schemas index. + * + * @param options - Global options + * @returns {McpResource} The resource definition tuple + */ +const patternFlyComponentsIndexResource = (options = getOptions()): McpResource => [ + NAME, + new ResourceTemplate(URI_TEMPLATE, { + list: async () => runWithOptions(options, async () => listResources.memo()), + complete: { + version: async (...args) => runWithOptions(options, async () => uriVersionComplete(...args)) + } + }), + CONFIG, + async (uri, variables) => runWithOptions(options, async () => resourceCallback(uri, variables)) +]; + +export { + patternFlyComponentsIndexResource, + listResources, + resourceCallback, + uriVersionComplete, + NAME, + URI_TEMPLATE, + CONFIG +}; diff --git a/src/resource.patternFlyContext.ts b/src/resource.patternFlyContext.ts index fd306e4b..3fba6ffe 100644 --- a/src/resource.patternFlyContext.ts +++ b/src/resource.patternFlyContext.ts @@ -1,4 +1,7 @@ import { type McpResource } from './server'; +import { getPatternFlyMcpResources } from './patternFly.getResources'; +import { getOptions } from './options.context'; +import { stringJoin } from './server.helpers'; /** * Name of the resource. @@ -22,13 +25,15 @@ const CONFIG = { /** * Resource creator for context. * + * @param options - Global options * @returns {McpResource} The resource definition tuple */ -const patternFlyContextResource = (): McpResource => [ +const patternFlyContextResource = (options = getOptions()): McpResource => [ NAME, URI_TEMPLATE, CONFIG, async () => { + const { envVersion } = await getPatternFlyMcpResources.memo(); const context = `PatternFly is an open-source design system for building consistent, accessible user interfaces. **What is PatternFly?** @@ -43,12 +48,20 @@ PatternFly provides React components, design guidelines, and development tools f **PatternFly MCP Server:** This MCP server provides tools to access PatternFly documentation, component schemas, and design guidelines. Use the available tools to fetch documentation, search for component information, and retrieve component prop definitions.`; + const environmentSnapshot = stringJoin.newline( + `### Environment Snapshot`, + '', + `**PatternFly Version:** ${envVersion}`, + // `**Detected PatternFly SemVer:** ${closestSemVer}`, + `**Context Path**: ${options.contextPath}` + ); + return { contents: [ { uri: 'patternfly://context', mimeType: 'text/markdown', - text: context + text: stringJoin.basic(context, options.separator, environmentSnapshot) } ] }; diff --git a/src/resource.patternFlyDocsIndex.ts b/src/resource.patternFlyDocsIndex.ts index b212cf92..5da02cdd 100644 --- a/src/resource.patternFlyDocsIndex.ts +++ b/src/resource.patternFlyDocsIndex.ts @@ -1,9 +1,33 @@ -import { COMPONENT_DOCS } from './docs.component'; -import { LAYOUT_DOCS } from './docs.layout'; -import { CHART_DOCS } from './docs.chart'; -import { getLocalDocs } from './docs.local'; +import { + ResourceTemplate, + type CompleteResourceTemplateCallback +} from '@modelcontextprotocol/sdk/server/mcp.js'; import { type McpResource } from './server'; import { stringJoin } from './server.helpers'; +import { memo } from './server.caching'; +import { getPatternFlyMcpResources } from './patternFly.getResources'; +import { getOptions, runWithOptions } from './options.context'; +import { + filterEnumeratedPatternFlyVersions, + normalizeEnumeratedPatternFlyVersion +} from './patternFly.helpers'; + +/** + * List resources result type. + * + * @note This is temporary until MCP SDK exports ListResourcesResult. + * + * @property uri - The fully qualified URI of the resource. + * @property name - A human-readable name for the resource. + * @property [mimeType] - The MIME type of the content. + * @property [description] - A brief hint for the model. + */ +type PatterFlyListResourceResult = { + uri: string; + name: string; + mimeType?: string; + description?: string; +}; /** * Name of the resource. @@ -13,53 +37,237 @@ const NAME = 'patternfly-docs-index'; /** * URI template for the resource. */ -const URI_TEMPLATE = 'patternfly://docs/index'; +const URI_TEMPLATE = 'patternfly://docs/index{?version,section,category}'; /** * Resource configuration. */ const CONFIG = { title: 'PatternFly Documentation Index', - description: 'A comprehensive list of PatternFly documentation links, organized by components, layouts, charts, and local files.', + description: 'A comprehensive list of PatternFly documentation links, organized by components, layouts, charts, and guidance files.', mimeType: 'text/markdown' }; +/** + * List resources callback for the URI template. + * + * @returns {Promise} The list of available resources. + */ +const listResources = async () => { + const { byVersion } = await getPatternFlyMcpResources.memo(); + const resources: PatterFlyListResourceResult[] = []; + + // Initial sort by the latest version + Object.entries(byVersion).sort(([a], [b]) => b.localeCompare(a)).forEach(([version, entries]) => { + const seenIndex = new Set(); + const versionResource: PatterFlyListResourceResult[] = []; + + entries.forEach(entry => { + if (!seenIndex.has(entry.name)) { + seenIndex.add(entry.name); + + versionResource.push({ + uri: `patternfly://docs/${version}/${entry.name.toLowerCase()}`, + mimeType: 'text/markdown', + name: `${entry.name} (${version})`, + description: `Documentation for PatternFly version "${version}" of "${entry.name}"` + }); + } + }); + + resources.push(...versionResource); + }); + + return { + resources + }; +}; + +/** + * Memoized version of listResources. + */ +listResources.memo = memo(listResources); + +/** + * Category completion callback for the URI template. + * + * @param value - The value to filter-by/complete. + * @param context - The completion context containing arguments for the URI template. + * @returns The list of available categories. + */ +const uriCategoryComplete: CompleteResourceTemplateCallback = async (value: unknown, context) => { + const { version, section } = context?.arguments || {}; + const normalizedSection = typeof section === 'string' ? section?.trim()?.toLowerCase() : undefined; + let updatedVersion = await normalizeEnumeratedPatternFlyVersion.memo(version); + const { latestVersion, byVersion } = await getPatternFlyMcpResources.memo(); + + if (!updatedVersion) { + updatedVersion = latestVersion; + } + + const entries = byVersion[updatedVersion] || []; + const availableCategories = new Set(); + + entries.forEach(entry => { + if (normalizedSection && entry.section.toLowerCase() === normalizedSection) { + availableCategories.add(entry.category); + + return; + } + + availableCategories.add(entry.category); + }); + + return Array.from(availableCategories).sort(); +}; + +/** + * Section completion callback for the URI template. + * + * @param value - The value to filter-by/complete. + * @param context - The completion context containing arguments for the URI template. + * @returns The list of available sections. + */ +const uriSectionComplete: CompleteResourceTemplateCallback = async (value: unknown, context) => { + const { version, category } = context?.arguments || {}; + const normalizedCategory = typeof category === 'string' ? category?.trim()?.toLowerCase() : undefined; + let updatedVersion = await normalizeEnumeratedPatternFlyVersion.memo(version); + const { latestVersion, byVersion } = await getPatternFlyMcpResources.memo(); + + if (!updatedVersion) { + updatedVersion = latestVersion; + } + + const entries = byVersion[updatedVersion] || []; + const availableSections = new Set(); + + entries.forEach(entry => { + if (normalizedCategory && entry.category.toLowerCase() === normalizedCategory) { + availableSections.add(entry.section); + + return; + } + + availableSections.add(entry.section); + }); + + return Array.from(availableSections).sort(); +}; + +/** + * Name completion callback for the URI template. + * + * @param value - The value to complete. + * @returns The list of available versions. + */ +const uriVersionComplete: CompleteResourceTemplateCallback = async (value: unknown) => + filterEnumeratedPatternFlyVersions(value as string | undefined); + +/** + * Resource callback for the documentation index. + * + * @param uri - URI of the resource. + * @param variables - Variables for the resource. + * @returns The resource contents. + */ +const resourceCallback = async (uri: URL, variables: Record) => { + const { category, version, section } = variables || {}; + let updatedVersion = await normalizeEnumeratedPatternFlyVersion.memo(version); + const { latestVersion, byVersion } = await getPatternFlyMcpResources.memo(); + + if (!updatedVersion) { + updatedVersion = latestVersion; + } + + let entries = byVersion[updatedVersion] || []; + + if (category || section) { + const normalizedCategory = typeof category === 'string' ? category.trim().toLowerCase() : undefined; + const normalizedSection = typeof section === 'string' ? section.trim().toLowerCase() : undefined; + + entries = entries.filter(entry => { + const matchesCategory = category && entry.category.toLowerCase() === normalizedCategory; + const matchesSection = section && entry.section.toLowerCase() === normalizedSection; + + if (normalizedCategory && normalizedSection) { + return matchesCategory && matchesSection; + } else { + return matchesCategory || matchesSection; + } + }); + } + + // Group by URI + const groupedByUri = new Map(); + + entries.forEach(item => { + if (!groupedByUri.has(item.uri)) { + groupedByUri.set(item.uri, { + name: item.name, + version: item.version, + categories: [item.displayCategory] + }); + } else { + groupedByUri.get(item.uri)?.categories.push(item.displayCategory); + } + }); + + // Generate the consolidated list + const docsIndex = Array.from(groupedByUri.entries()) + .sort(([_aUri, aData], [_bUri, bData]) => aData.name.localeCompare(bData.name)) + .map(([uri, data], index) => { + const categoryList = data.categories.join(', '); + + return `${index + 1}. [${data.name} - ${categoryList} (${data.version})](${uri})`; + }); + + const allDocs = stringJoin.newline( + `# PatternFly Documentation Index for "${updatedVersion}"`, + '', + '', + ...(docsIndex || []) + ); + + return { + contents: [ + { + uri: 'patternfly://docs/index', + mimeType: 'text/markdown', + text: allDocs + } + ] + }; +}; + /** * Resource creator for the documentation index. * + * @param options - Global options * @returns {McpResource} The resource definition tuple */ -const patternFlyDocsIndexResource = (): McpResource => [ +const patternFlyDocsIndexResource = (options = getOptions()): McpResource => [ NAME, - URI_TEMPLATE, + new ResourceTemplate(URI_TEMPLATE, { + list: async () => runWithOptions(options, async () => listResources.memo()), + complete: { + category: async (...args) => runWithOptions(options, async () => uriCategoryComplete(...args)), + section: async (...args) => runWithOptions(options, async () => uriSectionComplete(...args)), + version: async (...args) => runWithOptions(options, async () => uriVersionComplete(...args)) + } + }), CONFIG, - async () => { - const allDocs = stringJoin.newline( - '# PatternFly Documentation Index', - '', - '## Components', - ...COMPONENT_DOCS, - '', - '## Layouts', - ...LAYOUT_DOCS, - '', - '## Charts', - ...CHART_DOCS, - '', - '## Local Documentation', - ...getLocalDocs() - ); - - return { - contents: [ - { - uri: 'patternfly://docs/index', - mimeType: 'text/markdown', - text: allDocs - } - ] - }; - } + async (uri, variables) => runWithOptions(options, async () => resourceCallback(uri, variables)) ]; -export { patternFlyDocsIndexResource, NAME, URI_TEMPLATE, CONFIG }; +export { + patternFlyDocsIndexResource, + listResources, + resourceCallback, + uriCategoryComplete, + uriSectionComplete, + uriVersionComplete, + NAME, + URI_TEMPLATE, + CONFIG, + type PatterFlyListResourceResult +}; diff --git a/src/resource.patternFlyDocsTemplate.ts b/src/resource.patternFlyDocsTemplate.ts index c15d05fe..f35afab2 100644 --- a/src/resource.patternFlyDocsTemplate.ts +++ b/src/resource.patternFlyDocsTemplate.ts @@ -1,11 +1,16 @@ -import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { + ResourceTemplate, + type CompleteResourceTemplateCallback +} from '@modelcontextprotocol/sdk/server/mcp.js'; import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; import { type McpResource } from './server'; import { processDocsFunction } from './server.getResources'; -import { searchComponents } from './tool.searchPatternFlyDocs'; -import { getOptions } from './options.context'; -import { memo } from './server.caching'; import { stringJoin } from './server.helpers'; +import { getOptions, runWithOptions } from './options.context'; +import { searchPatternFly } from './patternFly.search'; +import { getPatternFlyMcpResources } from './patternFly.getResources'; +import { normalizeEnumeratedPatternFlyVersion } from './patternFly.helpers'; +import { listResources, uriVersionComplete } from './resource.patternFlyDocsIndex'; /** * Name of the resource template. @@ -15,7 +20,7 @@ const NAME = 'patternfly-docs-template'; /** * URI template for the resource. */ -const URI_TEMPLATE = new ResourceTemplate('patternfly://docs/{name}', { list: undefined }); +const URI_TEMPLATE = 'patternfly://docs/{version}/{name}{?section,category}'; /** * Resource configuration. @@ -27,93 +32,153 @@ const CONFIG = { }; /** - * Resource creator for the documentation template. + * Name completion callback for the URI template. * - * @param options - Global options - * @returns {McpResource} The resource definition tuple + * @note If version is not available, the latest version is used to refine the search results + * since it aligns with the default behavior of the PatternFly documentation. + * + * @param value - The value to complete. + * @param context - The completion context. + * @returns The list of available names. */ -const patternFlyDocsTemplateResource = (options = getOptions()): McpResource => { - const memoProcess = memo(processDocsFunction, options?.toolMemoOptions?.usePatternFlyDocs); - - return [ - NAME, - URI_TEMPLATE, - CONFIG, - async (uri: URL, variables: Record) => { - const { name } = variables || {}; - - if (!name || typeof name !== 'string') { - throw new McpError( - ErrorCode.InvalidParams, - `Missing required parameter: name must be a string: ${name}` - ); - } - - if (name.length > options.maxSearchLength) { - throw new McpError( - ErrorCode.InvalidParams, - `Resource name exceeds maximum length of ${options.maxSearchLength} characters.` - ); - } +const uriNameComplete: CompleteResourceTemplateCallback = async (value: unknown, context) => { + const { latestVersion, byVersion } = await getPatternFlyMcpResources.memo(); + const version = context?.arguments?.version; + const updatedVersion = (await normalizeEnumeratedPatternFlyVersion.memo(version)) || latestVersion; + const updatedValue = typeof value === 'string' ? value.toLowerCase().trim() : ''; + const names = new Set(); - const docResults = []; - const docs = []; - const { exactMatches, searchResults } = searchComponents.memo(name); + byVersion[updatedVersion]?.filter(entry => entry.name.toLowerCase().startsWith(updatedValue)) + .forEach(entry => names.add(entry.name)); - if (exactMatches.length === 0 || exactMatches.every(match => match.urls.length === 0)) { - const suggestions = searchResults.map(searchResult => searchResult.item).slice(0, 3); - const suggestionMessage = suggestions.length - ? `Did you mean ${suggestions.map(suggestion => `"${suggestion}"`).join(', ')}?` - : 'No similar components found.'; - - throw new McpError( - ErrorCode.InvalidParams, - `No documentation found for component "${name.trim()}". ${suggestionMessage}` - ); - } + return Array.from(names).sort(); +}; - try { - const exactMatchesUrls = exactMatches.flatMap(match => match.urls); +/** + * Resource callback for the documentation template. + * + * @param uri - URI of the resource. + * @param variables - Variables for the resource. + * @param options - Global options + * @returns The resource contents. + */ +const resourceCallback = async (uri: URL, variables: Record, options = getOptions()) => { + const { version, name } = variables || {}; + + if (!name || typeof name !== 'string') { + throw new McpError( + ErrorCode.InvalidParams, + `Missing required parameter: name must be a string: ${name}` + ); + } + + if (name.length > options.maxSearchLength) { + throw new McpError( + ErrorCode.InvalidParams, + `Resource name exceeds maximum length of ${options.maxSearchLength} characters.` + ); + } + + let updatedVersion = await normalizeEnumeratedPatternFlyVersion.memo(version); + + if (!updatedVersion) { + const { latestVersion } = await getPatternFlyMcpResources.memo(); + + updatedVersion = latestVersion; + } + + const docResults = []; + const docs = []; + const { searchResults, exactMatches } = await searchPatternFly.memo(name); + + if (exactMatches.length === 0 || exactMatches.every(match => !match.versions[updatedVersion]?.urls.length)) { + const { resources } = await getPatternFlyMcpResources.memo(); + const isSchemasAvailable = resources.get(name.toLowerCase())?.versions?.[updatedVersion]?.isSchemasAvailable; + let suggestionMessage; + + if (isSchemasAvailable) { + suggestionMessage = + `A JSON Schema is available. Use "patternfly://schemas/${updatedVersion}/${name.toLowerCase()}" to view prop definitions."`; + } else { + const suggestions = searchResults.map(result => result.item).slice(0, 3); + + suggestionMessage = suggestions.length + ? `Did you mean ${suggestions.map(suggestion => `"${suggestion}"`).join(', ')}?` + : 'No similar resources found.'; + } - if (exactMatchesUrls.length > 0) { - const processedDocs = await memoProcess(exactMatchesUrls); + throw new McpError( + ErrorCode.InvalidParams, + `No documentation found for "${name.trim()}". ${suggestionMessage}` + ); + } - docs.push(...processedDocs); - } - } catch (error) { - throw new McpError( - ErrorCode.InternalError, - `Failed to fetch documentation: ${error}` - ); - } + try { + const exactMatchesUrls = exactMatches.flatMap(match => match.versions[updatedVersion]?.urls).filter(Boolean) as string[]; - // Redundancy check, technically this should never happen, future proofing - if (docs.length === 0) { - throw new McpError( - ErrorCode.InvalidParams, - `Component "${name.trim()}" was found, but no documentation URLs are available for it.` - ); - } + if (exactMatchesUrls.length > 0) { + const processedDocs = await processDocsFunction.memo(exactMatchesUrls); - for (const doc of docs) { - docResults.push(stringJoin.newline( - `# Documentation from ${doc.resolvedPath || doc.path}`, - '', - doc.content - )); + docs.push(...processedDocs); + } + } catch (error) { + throw new McpError( + ErrorCode.InternalError, + `Failed to fetch documentation: ${error}` + ); + } + + // Redundancy check, technically this should never happen, future proofing + if (docs.length === 0) { + throw new McpError( + ErrorCode.InvalidParams, + `"${name.trim()}" was found, but no documentation URLs are available for it.` + ); + } + + for (const doc of docs) { + docResults.push(stringJoin.newline( + `# Documentation from ${doc.resolvedPath || doc.path}`, + '', + doc.content + )); + } + + return { + contents: [ + { + uri: uri.href, + mimeType: 'text/markdown', + text: docResults.join(options.separator) } + ] + }; +}; - return { - contents: [ - { - uri: uri.href, - mimeType: 'text/markdown', - text: docResults.join(options.separator) - } - ] - }; +/** + * Resource creator for the documentation template. + * + * @param options - Global options + * @returns {McpResource} The resource definition tuple + */ +const patternFlyDocsTemplateResource = (options = getOptions()): McpResource => [ + NAME, + new ResourceTemplate(URI_TEMPLATE, { + list: async () => runWithOptions(options, async () => listResources.memo()), + complete: { + version: async (...args) => runWithOptions(options, async () => uriVersionComplete(...args)), + name: async (...args) => runWithOptions(options, async () => uriNameComplete(...args)) } - ]; + }), + CONFIG, + async (uri, variables) => runWithOptions(options, async () => resourceCallback(uri, variables, options)) +]; + +export { + patternFlyDocsTemplateResource, + resourceCallback, + uriNameComplete, + NAME, + URI_TEMPLATE, + CONFIG }; - -export { patternFlyDocsTemplateResource, NAME, URI_TEMPLATE, CONFIG }; diff --git a/src/resource.patternFlySchemasIndex.ts b/src/resource.patternFlySchemasIndex.ts index bbb8ec0d..daedb265 100644 --- a/src/resource.patternFlySchemasIndex.ts +++ b/src/resource.patternFlySchemasIndex.ts @@ -1,6 +1,14 @@ -import { componentNames } from '@patternfly/patternfly-component-schemas/json'; +import { + type CompleteResourceTemplateCallback, + ResourceTemplate +} from '@modelcontextprotocol/sdk/server/mcp.js'; import { type McpResource } from './server'; +import { memo } from './server.caching'; import { stringJoin } from './server.helpers'; +import { getOptions, runWithOptions } from './options.context'; +import { getPatternFlyMcpResources } from './patternFly.getResources'; +import { type PatterFlyListResourceResult } from './resource.patternFlyDocsIndex'; +import { normalizeEnumeratedPatternFlyVersion } from './patternFly.helpers'; /** * Name of the resource. @@ -10,7 +18,7 @@ const NAME = 'patternfly-schemas-index'; /** * URI template for the resource. */ -const URI_TEMPLATE = 'patternfly://schemas/index'; +const URI_TEMPLATE = 'patternfly://schemas/index{?version}'; /** * Resource configuration. @@ -22,26 +30,136 @@ const CONFIG = { }; /** - * Resource creator for the component schemas index. + * List resources callback for the URI template. * - * @returns {McpResource} The resource definition tuple + * @returns {Promise} The list of available resources. */ -const patternFlySchemasIndexResource = (): McpResource => [ - NAME, - URI_TEMPLATE, - CONFIG, - async () => ({ +const listResources = async () => { + const { availableSchemasVersions, byVersion, resources: docsResources } = await getPatternFlyMcpResources.memo(); + + const resources: PatterFlyListResourceResult[] = []; + + availableSchemasVersions.forEach(version => { + const versionEntries = byVersion[version] || []; + const versionResource: PatterFlyListResourceResult[] = []; + const seenIndex = new Set(); + + versionEntries.forEach(entry => { + const entryName = entry.name.toLowerCase(); + + if (!seenIndex.has(entryName) && docsResources.get(entryName)?.versions?.[version]?.isSchemasAvailable) { + seenIndex.add(entryName); + + resources.push({ + uri: `patternfly://schemas/${version}/${entryName}`, + mimeType: 'application/json', + name: `${entry.name} (${version})`, + description: `JSON component schemas for PatternFly version "${version}" of "${entry.name}"` + }); + } + }); + + resources.push(...versionResource); + }); + + return { + resources + }; +}; + +/** + * Memoized version of listResources. + */ +listResources.memo = memo(listResources); + +/** + * Name completion callback for the URI template. + * + * @note Currently component schemas are limited to `v6` so they receive a + * custom available version index. + * + * @param _value - The value to complete. + * @returns The list of available versions. + */ +const uriVersionComplete: CompleteResourceTemplateCallback = async (_value: unknown) => { + const { availableSchemasVersions } = await getPatternFlyMcpResources.memo(); + + return availableSchemasVersions; +}; + +/** + * Resource callback for the documentation index. + * + * @param uri - URI of the resource. + * @param variables - Variables for the resource. + * @returns The resource contents. + */ +const resourceCallback = async (uri: URL, variables: Record) => { + const { version } = variables || {}; + let updatedVersion = await normalizeEnumeratedPatternFlyVersion.memo(version); + const { latestVersion, byVersion, resources } = await getPatternFlyMcpResources.memo(); + + if (!updatedVersion) { + updatedVersion = latestVersion; + } + + const entries = byVersion[updatedVersion] || []; + + // Group by URI + const groupedByUri = new Map(); + + entries.forEach(entry => { + const entryName = entry.name.toLowerCase(); + const resource = resources.get(entryName)?.versions[updatedVersion]; + + if (resource?.uriSchemas) { + groupedByUri.set(resource.uriSchemas, { name: entry.name, version: entry.version }); + } + }); + + // Generate the consolidated list + const docsIndex = Array.from(groupedByUri.entries()) + .sort(([_aUri, aData], [_bUri, bData]) => aData.name.localeCompare(bData.name)) + .map(([uri, data], index) => `${index + 1}. [${data.name} (${data.version})](${uri})`); + + return { contents: [{ uri: 'patternfly://schemas/index', mimeType: 'text/markdown', text: stringJoin.newline( - '# PatternFly Component Names Index', + `# PatternFly Component JSON Schemas Index for "${updatedVersion}"`, '', '', - ...componentNames + ...docsIndex || [] ) }] - }) + }; +}; + +/** + * Resource creator for the component schemas index. + * + * @param options - Global options + * @returns {McpResource} The resource definition tuple + */ +const patternFlySchemasIndexResource = (options = getOptions()): McpResource => [ + NAME, + new ResourceTemplate(URI_TEMPLATE, { + list: async () => runWithOptions(options, async () => listResources.memo()), + complete: { + version: async (...args) => runWithOptions(options, async () => uriVersionComplete(...args)) + } + }), + CONFIG, + async (uri, variables) => runWithOptions(options, async () => resourceCallback(uri, variables)) ]; -export { patternFlySchemasIndexResource, NAME, URI_TEMPLATE, CONFIG }; +export { + patternFlySchemasIndexResource, + listResources, + resourceCallback, + uriVersionComplete, + NAME, + URI_TEMPLATE, + CONFIG +}; diff --git a/src/resource.patternFlySchemasTemplate.ts b/src/resource.patternFlySchemasTemplate.ts index ac051bf6..8ec4db81 100644 --- a/src/resource.patternFlySchemasTemplate.ts +++ b/src/resource.patternFlySchemasTemplate.ts @@ -1,15 +1,18 @@ -import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { + type CompleteResourceTemplateCallback, + ResourceTemplate +} from '@modelcontextprotocol/sdk/server/mcp.js'; import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; -import { componentNames as pfComponentNames } from '@patternfly/patternfly-component-schemas/json'; import { type McpResource } from './server'; -import { getOptions } from './options.context'; -import { getComponentSchema } from './tool.patternFlyDocs'; -import { searchComponents } from './tool.searchPatternFlyDocs'; - -/** - * Derive the component schema type from @patternfly/patternfly-component-schemas - */ -type ComponentSchema = Awaited>; +import { getOptions, runWithOptions } from './options.context'; +import { searchPatternFly } from './patternFly.search'; +import { + getPatternFlyComponentSchema, + getPatternFlyMcpResources, + type PatternFlyComponentSchema +} from './patternFly.getResources'; +import { normalizeEnumeratedPatternFlyVersion } from './patternFly.helpers'; +import { listResources, uriVersionComplete } from './resource.patternFlySchemasIndex'; /** * Name of the resource template. @@ -19,7 +22,7 @@ const NAME = 'patternfly-schemas-template'; /** * URI template for the resource. */ -const URI_TEMPLATE = new ResourceTemplate('patternfly://schemas/{name}', { list: undefined }); +const URI_TEMPLATE = 'patternfly://schemas/{version}/{name}'; /** * Resource configuration. @@ -31,69 +34,128 @@ const CONFIG = { }; /** - * Resource creator for the component schemas template. + * Name completion callback for the URI template. + * + * @param value - The value to complete. + * @param context - The completion context. + * @returns The list of available names. + */ +const uriNameComplete: CompleteResourceTemplateCallback = async (value: unknown, context) => { + const { latestSchemasVersion, byVersion, resources } = await getPatternFlyMcpResources.memo(); + const version = context?.arguments?.version; + const updatedVersion = (await normalizeEnumeratedPatternFlyVersion.memo(version)) || latestSchemasVersion; + const updatedValue = typeof value === 'string' ? value.toLowerCase().trim() : ''; + const names = new Set(); + + byVersion[updatedVersion]?.filter(entry => { + const entryName = entry.name.toLowerCase(); + + return resources.get(entryName)?.versions?.[updatedVersion]?.isSchemasAvailable && entryName.startsWith(updatedValue); + }).forEach(entry => names.add(entry.name)); + + return Array.from(names).sort(); +}; + +/** + * Resource callback for the documentation template. * + * @note We temporarily use `DEFAULT_OPTIONS` `latestSchemasVersion` + * + * @param uri - The URI of the resource. + * @param variables - The variables of the resource. * @param options - Global options - * @returns {McpResource} The resource definition tuple + * @returns The resource contents. */ -const patternFlySchemasTemplateResource = (options = getOptions()): McpResource => [ - NAME, - URI_TEMPLATE, - CONFIG, - async (uri: URL, variables: Record) => { - const { name } = variables || {}; - - if (!name || typeof name !== 'string') { - throw new McpError( - ErrorCode.InvalidParams, - `Missing required parameter: name must be a string: ${name}` - ); - } +const resourceCallback = async (uri: URL, variables: Record, options = getOptions()) => { + const { version, name } = variables || {}; - if (name.length > options.maxSearchLength) { - throw new McpError( - ErrorCode.InvalidParams, - `Resource name exceeds maximum length of ${options.maxSearchLength} characters.` - ); - } + if (!name || typeof name !== 'string') { + throw new McpError( + ErrorCode.InvalidParams, + `Missing required parameter: name must be a string: ${name}` + ); + } - const { exactMatches, searchResults } = searchComponents.memo(name, { names: pfComponentNames }); - let result: ComponentSchema | undefined = undefined; + if (name.length > options.maxSearchLength) { + throw new McpError( + ErrorCode.InvalidParams, + `Resource name exceeds maximum length of ${options.maxSearchLength} characters.` + ); + } + + let updatedVersion = await normalizeEnumeratedPatternFlyVersion.memo(version); - if (exactMatches.length > 0) { - for (const match of exactMatches) { - const schema = await getComponentSchema.memo(match.item); + if (!updatedVersion) { + const { latestSchemasVersion } = await getPatternFlyMcpResources.memo(); - if (schema) { - result = schema; - break; - } + updatedVersion = latestSchemasVersion; + } + + const { exactMatches, searchResults } = await searchPatternFly.memo(name); + let result: PatternFlyComponentSchema | undefined; + + if (exactMatches.length > 0) { + for (const match of exactMatches) { + const schema = await getPatternFlyComponentSchema.memo(match.item); + + if (schema) { + result = schema; + break; } } + } - if (result === undefined) { - const suggestions = searchResults.map(searchResult => searchResult.item).slice(0, 3); - const suggestionMessage = suggestions.length - ? `Did you mean ${suggestions.map(suggestion => `"${suggestion}"`).join(', ')}?` - : 'No similar components found.'; - const foundNotFound = exactMatches.length ? 'found but JSON schema not available.' : 'not found.'; - - throw new McpError( - ErrorCode.InvalidParams, - `Component "${name.trim()}" ${foundNotFound} ${suggestionMessage}` - ); - } + if (result === undefined) { + const suggestions = searchResults + .filter(searchResult => searchResult?.versions?.[updatedVersion]?.isSchemasAvailable) + .map(searchResult => searchResult.item).slice(0, 3); + + const suggestionMessage = suggestions.length + ? `Did you mean ${suggestions.map(suggestion => `"${suggestion}"`).join(', ')}?` + : 'No similar components found.'; + const foundNotFound = exactMatches.length ? 'found but JSON schema not available.' : 'not found.'; - return { - contents: [ - { - uri: uri.href, - mimeType: 'application/json', - text: JSON.stringify(result, null, 2) - } - ] - }; + throw new McpError( + ErrorCode.InvalidParams, + `Component "${name.trim()}" ${foundNotFound} ${suggestionMessage}` + ); } + + return { + contents: [ + { + uri: uri.href, + mimeType: 'application/json', + text: JSON.stringify(result, null, 2) + } + ] + }; +}; + +/** + * Resource creator for the component schemas template. + * + * @param options - Global options + * @returns {McpResource} The resource definition tuple + */ +const patternFlySchemasTemplateResource = (options = getOptions()): McpResource => [ + NAME, + new ResourceTemplate(URI_TEMPLATE, { + list: async () => runWithOptions(options, async () => listResources.memo()), + complete: { + name: async (...args) => runWithOptions(options, async () => uriNameComplete(...args)), + version: async (...args) => runWithOptions(options, async () => uriVersionComplete(...args)) + } + }), + CONFIG, + async (uri, variables) => runWithOptions(options, async () => resourceCallback(uri, variables, options)) ]; -export { patternFlySchemasTemplateResource, NAME, URI_TEMPLATE, CONFIG }; +export { + patternFlySchemasTemplateResource, + resourceCallback, + uriNameComplete, + NAME, + URI_TEMPLATE, + CONFIG +}; diff --git a/src/server.ts b/src/server.ts index c53ec599..88d49176 100644 --- a/src/server.ts +++ b/src/server.ts @@ -3,6 +3,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import { usePatternFlyDocsTool } from './tool.patternFlyDocs'; import { searchPatternFlyDocsTool } from './tool.searchPatternFlyDocs'; import { componentSchemasTool } from './tool.componentSchemas'; +import { patternFlyComponentsIndexResource } from './resource.patternFlyComponentsIndex'; import { patternFlyContextResource } from './resource.patternFlyContext'; import { patternFlyDocsIndexResource } from './resource.patternFlyDocsIndex'; import { patternFlyDocsTemplateResource } from './resource.patternFlyDocsTemplate'; @@ -158,6 +159,7 @@ const builtinTools: McpToolCreator[] = [ */ const builtinResources: McpResourceCreator[] = [ patternFlyContextResource, + patternFlyComponentsIndexResource, patternFlyDocsIndexResource, patternFlyDocsTemplateResource, patternFlySchemasIndexResource, @@ -241,6 +243,7 @@ const runServer = async (options: ServerOptions = getOptions(), { capabilities: { tools: {}, resources: {}, + completions: {}, ...(enableProtocolLogging ? { logging: {} } : {}) } } diff --git a/src/tool.componentSchemas.ts b/src/tool.componentSchemas.ts index 7da03238..58b5320d 100644 --- a/src/tool.componentSchemas.ts +++ b/src/tool.componentSchemas.ts @@ -1,16 +1,13 @@ import { z } from 'zod'; import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; -import { getComponentSchema } from '@patternfly/patternfly-component-schemas/json'; import { type McpTool } from './server'; -import { getOptions } from './options.context'; -import { memo } from './server.caching'; import { fuzzySearch } from './server.search'; -import { componentNames } from './tool.searchPatternFlyDocs'; - -/** - * Derive the component schema type from @patternfly/patternfly-component-schemas - */ -type ComponentSchema = Awaited>; +import { getOptions } from './options.context'; +import { + getPatternFlyComponentSchema, + getPatternFlyReactComponentNames, + type PatternFlyComponentSchema +} from './patternFly.getResources'; /** * componentSchemas tool function @@ -22,13 +19,9 @@ type ComponentSchema = Awaited>; * @returns MCP tool tuple [name, schema, callback] */ const componentSchemasTool = (options = getOptions()): McpTool => { - const memoGetComponentSchema = memo( - async (componentName: string): Promise => getComponentSchema(componentName), - options?.toolMemoOptions?.usePatternFlyDocs - ); - const callback = async (args: any = {}) => { const { componentName } = args; + const { componentNamesWithSchemasIndex: componentNames } = await getPatternFlyReactComponentNames.memo(); if (typeof componentName !== 'string') { throw new McpError( @@ -55,10 +48,14 @@ const componentSchemasTool = (options = getOptions()): McpTool => { const exact = results.find(result => result.matchType === 'exact'); if (exact) { - let componentSchema: ComponentSchema; + let componentSchema: PatternFlyComponentSchema | undefined; try { - componentSchema = await memoGetComponentSchema(exact.item); + componentSchema = await getPatternFlyComponentSchema.memo(exact.item); + + if (componentSchema === undefined) { + throw new Error(`Component schema for "${exact.item}" doesn't exist.`); + } } catch (error) { throw new McpError( ErrorCode.InternalError, diff --git a/src/tool.patternFlyDocs.ts b/src/tool.patternFlyDocs.ts index 54c76adf..9bf04148 100644 --- a/src/tool.patternFlyDocs.ts +++ b/src/tool.patternFlyDocs.ts @@ -1,33 +1,14 @@ import { z } from 'zod'; import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; -import { getComponentSchema as pfGetComponentSchema } from '@patternfly/patternfly-component-schemas/json'; import { type McpTool } from './server'; -import { getOptions } from './options.context'; -import { processDocsFunction } from './server.getResources'; -import { memo } from './server.caching'; +import { processDocsFunction, type ProcessedDoc } from './server.getResources'; import { stringJoin } from './server.helpers'; -import { setComponentToDocsMap, searchComponents } from './tool.searchPatternFlyDocs'; -import { DEFAULT_OPTIONS } from './options.defaults'; +import { getOptions } from './options.context'; +import { searchPatternFly } from './patternFly.search'; +import { getPatternFlyMcpResources, getPatternFlyComponentSchema, setCategoryDisplayLabel } from './patternFly.getResources'; +import { normalizeEnumeratedPatternFlyVersion } from './patternFly.helpers'; import { log } from './logger'; -/** - * Get the component schema from @patternfly/patternfly-component-schemas. - * - * @param componentName - */ -const getComponentSchema = async (componentName: string) => { - try { - return await pfGetComponentSchema(componentName); - } catch {} - - return undefined; -}; - -/** - * Memoized version of getComponentSchema. - */ -getComponentSchema.memo = memo(getComponentSchema, DEFAULT_OPTIONS.toolMemoOptions.usePatternFlyDocs); - /** * usePatternFlyDocs tool function * @@ -35,20 +16,18 @@ getComponentSchema.memo = memo(getComponentSchema, DEFAULT_OPTIONS.toolMemoOptio * @returns MCP tool tuple [name, schema, callback] */ const usePatternFlyDocsTool = (options = getOptions()): McpTool => { - const memoProcess = memo(processDocsFunction, options?.toolMemoOptions?.usePatternFlyDocs); - const { getKey: getComponentToDocsKey } = setComponentToDocsMap.memo(); - const callback = async (args: any = {}) => { - const { urlList, name } = args; + const { urlList, name, version } = args; const isUrlList = urlList && Array.isArray(urlList) && urlList.length > 0 && urlList.every(url => typeof url === 'string' && url.trim().length > 0); const isName = typeof name === 'string' && name.trim().length > 0; + const isVersion = typeof version === 'string' && version.trim().length > 0; const hasUri = (isName && new RegExp('patternfly://', 'i').test(name)) || (isUrlList && urlList.some(url => new RegExp('patternfly://', 'i').test(url))); if (hasUri) { throw new McpError( ErrorCode.InvalidParams, stringJoin.basic( - 'Direct "patternfly://" URIs are not supported as tool inputs, and are intended to be used directly.', + 'Direct "patternfly://" URIs are not supported as tool inputs, and are intended to be used with MCP resources directly.', 'Use a component "name" or provide a "urlList" of raw documentation URLs.' ) ); @@ -68,6 +47,13 @@ const usePatternFlyDocsTool = (options = getOptions()): McpTool => { ); } + if (isVersion && version.length > options.maxSearchLength) { + throw new McpError( + ErrorCode.InvalidParams, + `String "version" exceeds maximum length of ${options.maxSearchLength} characters.` + ); + } + const updatedUrlList = isUrlList ? urlList.slice(0, options.recommendedMaxDocsToLoad) : []; if (isUrlList && urlList.length > options.recommendedMaxDocsToLoad) { @@ -76,31 +62,44 @@ const usePatternFlyDocsTool = (options = getOptions()): McpTool => { ); } - if (name) { - const { exactMatches, searchResults } = searchComponents.memo(name); + const { latestVersion, byPath } = await getPatternFlyMcpResources.memo(); + const updatedVersion = (await normalizeEnumeratedPatternFlyVersion(version)) || latestVersion; + const isLatestVersion = latestVersion === updatedVersion; - if (exactMatches.length === 0 || exactMatches.every(match => match.urls.length === 0)) { + const filteredUrlList = updatedUrlList.filter(url => byPath[url]?.version === updatedVersion); + + updatedUrlList.length = 0; + updatedUrlList.push(...filteredUrlList); + + const updatedName = name?.trim(); + + if (updatedName) { + const { searchResults, exactMatches } = await searchPatternFly.memo(updatedName); + + if (exactMatches.length === 0 || + exactMatches.every(match => !match.versions[updatedVersion]?.urls.length) + ) { const suggestions = searchResults.map(result => result.item).slice(0, 3); const suggestionMessage = suggestions.length ? `Did you mean ${suggestions.map(suggestion => `"${suggestion}"`).join(', ')}?` - : 'No similar components found.'; + : 'No similar resources found.'; throw new McpError( ErrorCode.InvalidParams, - `Component "${name.trim()}" not found. ${suggestionMessage}` + `Resource "${updatedName}" not found. ${suggestionMessage}` ); } - updatedUrlList.push(...exactMatches.flatMap(match => match.urls)); + updatedUrlList.push(...exactMatches.flatMap(match => match.versions[updatedVersion]?.urls).filter(Boolean)); } - const docs = []; + const docs: ProcessedDoc[] = []; const schemasSeen = new Set(); const schemaResults = []; const docResults = []; try { - const processedDocs = await memoProcess(updatedUrlList); + const processedDocs = await processDocsFunction.memo(updatedUrlList); docs.push(...processedDocs); } catch (error) { @@ -111,36 +110,49 @@ const usePatternFlyDocsTool = (options = getOptions()): McpTool => { } if (docs.length === 0) { + const nameFilter = `**Name**: ${name || '*'}`; + const versionFilter = `**PatternFly Version**: ${updatedVersion || '*'}`; const urlListBlock = updatedUrlList.map((url: string, index: number) => ` ${index + 1}. ${url}`).join('\n'); + const urlListFilter = stringJoin.newline( + `**URL List**:`, + urlListBlock || ' - None' + ); return { content: [{ type: 'text', text: stringJoin.newline( - `No PatternFly documentation found for:`, - urlListBlock, + `No PatternFly resources found for:`, + nameFilter, + versionFilter, + urlListFilter, '', '---', '', '**Important**:', - ' - To browse all available components use "searchPatternFlyDocs" with a search all ("*").' + ' - To browse all available resources use "searchPatternFlyDocs" with a search all ("*").' ) }] }; } for (const doc of docs) { - const componentName = getComponentToDocsKey(doc.path); + const patternFlyEntry = doc?.path ? byPath[doc.path] : undefined; + const componentName = patternFlyEntry?.name; + const docTitle = patternFlyEntry + ? `# Documentation for ${patternFlyEntry.displayName || componentName} [${setCategoryDisplayLabel(patternFlyEntry)}]` + : `# Content for ${doc.path}`; docResults.push(stringJoin.newline( - `# Documentation${(componentName && ` for ${componentName}`) || ''} from ${doc.path || 'unknown'}`, + docTitle, + `Source: ${doc.path}`, '', doc.content )); - if (componentName && !schemasSeen.has(componentName)) { + if (isLatestVersion && componentName && !schemasSeen.has(componentName)) { schemasSeen.add(componentName); - const componentSchema = await getComponentSchema.memo(componentName); + const componentSchema = await getPatternFlyComponentSchema.memo(componentName); if (componentSchema) { schemaResults.push(stringJoin.newline( @@ -167,18 +179,22 @@ const usePatternFlyDocsTool = (options = getOptions()): McpTool => { return [ 'usePatternFlyDocs', { - description: `Get markdown documentation and component JSON schemas for PatternFly components. + description: `Get markdown documentation and component JSON schemas for PatternFly resources and components. **Usage**: - 1. Input a component name (e.g., "Button") OR a list of up to ${options.recommendedMaxDocsToLoad} documentation URLs at a time (typically from searchPatternFlyDocs results). + 1. Input a resource or component name (e.g., "Button") OR a list of up to ${options.recommendedMaxDocsToLoad} documentation URLs at a time (typically from searchPatternFlyDocs results). **Returns**: - Markdown documentation - Component JSON schemas, if available `, inputSchema: { - urlList: z.array(z.string()).max(options.recommendedMaxDocsToLoad).optional().describe(`The list of URLs to fetch the documentation from (max ${options.recommendedMaxDocsToLoad} at a time`), - name: z.string().max(options.maxSearchLength).optional().describe('The name of a PatternFly component to fetch documentation for (e.g., "Button", "Table")') + urlList: z.array(z.string()).max(options.recommendedMaxDocsToLoad) + .optional().describe(`The list of URLs to fetch the documentation from (max ${options.recommendedMaxDocsToLoad} at a time`), + name: z.string().max(options.maxSearchLength) + .optional().describe('The name of a PatternFly resource or component to fetch documentation for (e.g., "Button", "Table")'), + version: z.enum(options.patternflyOptions.availableSearchVersions) + .optional().describe(`Filter results by a specific PatternFly version (e.g. ${options.patternflyOptions.availableSearchVersions.map(value => `"${value}"`).join(', ')})`) } }, callback @@ -190,4 +206,4 @@ const usePatternFlyDocsTool = (options = getOptions()): McpTool => { */ usePatternFlyDocsTool.toolName = 'usePatternFlyDocs'; -export { usePatternFlyDocsTool, getComponentSchema }; +export { usePatternFlyDocsTool }; diff --git a/src/tool.searchPatternFlyDocs.ts b/src/tool.searchPatternFlyDocs.ts index facbbebd..44dde493 100644 --- a/src/tool.searchPatternFlyDocs.ts +++ b/src/tool.searchPatternFlyDocs.ts @@ -1,193 +1,11 @@ import { z } from 'zod'; import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; -import { componentNames as pfComponentNames } from '@patternfly/patternfly-component-schemas/json'; import { type McpTool } from './server'; -import { COMPONENT_DOCS } from './docs.component'; -import { LAYOUT_DOCS } from './docs.layout'; -import { CHART_DOCS } from './docs.chart'; -import { getLocalDocs } from './docs.local'; -import { fuzzySearch, type FuzzySearchResult } from './server.search'; -import { getOptions } from './options.context'; -import { memo } from './server.caching'; import { stringJoin } from './server.helpers'; -import { DEFAULT_OPTIONS } from './options.defaults'; - -/** - * List of component names to include in search results. - * - * @note The "table" component is manually added to the list because it's not currently included - * in the component schemas package. - */ -const componentNames = [...pfComponentNames, 'Table'].sort((a, b) => a.localeCompare(b)); - -/** - * Extract a component name from an internal documentation URL string - * - * @note This is reliant on the documentation URLs being in the accepted format. - * If the format changes, this will need to be updated. This is a short-term solution - * until we can move the internal links to a new format like: - * ``` - * { - * name: 'Charts', - * description: 'Colors for Charts', - * type: 'example', - * scope: '@patternfly', - * url: `${PF_EXTERNAL_EXAMPLES_CHARTS}/ChartTheme/examples/ChartTheme.md` - * } - * ``` - * - * @example - * extractComponentName('[@patternfly/ComponentName - Type](URL)'); - * - * @param docUrl - Documentation URL string - * @returns ComponentName or `null` if not found - */ -const extractComponentName = (docUrl: string): string | null => { - // Stop at space or closing bracket, allowing dashes in the name - const match = docUrl.match(/\[@patternfly\/([^\s\]]+)/); - const name = match && match[1] ? match[1].trim() : null; - - // Filter out known non-component patterns - if (name?.startsWith('react-')) { - return null; - } - - return name; -}; - -/** - * Extract a URL from an internal Markdown link. - * - * @note This is a short-term solution until we can move the internal links to a new format. - * - * @example - * extractUrl('[text](URL)'); - * - * @param docUrl - * @returns URL or original string if not a Markdown link - */ -const extractUrl = (docUrl: string): string => { - const match = docUrl.match(/]\(([^)]+)\)/); - - return match && match[1] ? match[1] : docUrl; -}; - -/** - * Build a map of component names relative to internal documentation URLs. - * - * @returns Map of component name -> array of URLs (Design Guidelines + Accessibility) - */ -const setComponentToDocsMap = () => { - const map = new Map(); - const allDocs = [...COMPONENT_DOCS, ...LAYOUT_DOCS, ...CHART_DOCS, ...getLocalDocs()]; - const getKey = (value?: string | undefined) => { - if (!value) { - return undefined; - } - - for (const [key, urls] of map) { - if (urls.includes(value)) { - return key; - } else { - const results = fuzzySearch(value, urls, { - deduplicateByNormalized: true - }); - - if (results.length) { - return key; - } - } - } - - return undefined; - }; - - allDocs.forEach(docUrl => { - const componentName = extractComponentName(docUrl); - - if (componentName) { - const url = extractUrl(docUrl); - const existing = map.get(componentName) || []; - - map.set(componentName, [...existing, url]); - } - }); - - return { - map, - getKey - }; -}; - -/** - * Memoized version of componentToDocsMap. - */ -setComponentToDocsMap.memo = memo(setComponentToDocsMap); - -/** - * Search for PatternFly component documentation URLs using fuzzy search. - * - * @param searchQuery - Search query string - * @param settings - Optional settings object - * @param settings.names - List of names to search. Defaults to all component names. - * @param settings.allowWildCardAll - Allow a search query to match all components. Defaults to false. - * @returns Object containing search results and matched URLs - * - `isSearchWildCardAll`: Whether the search query matched all components - * - `firstExactMatch`: First exact match within fuzzy search results - * - `exactMatches`: All exact matches within fuzzy search results - * - `searchResults`: Fuzzy search results - */ -const searchComponents = (searchQuery: string, { names = componentNames, allowWildCardAll = false } = {}) => { - const isWildCardAll = searchQuery.trim() === '*' || searchQuery.trim().toLowerCase() === 'all' || searchQuery.trim() === ''; - const isSearchWildCardAll = allowWildCardAll && isWildCardAll; - const { map: componentToDocsMap } = setComponentToDocsMap.memo(); - let searchResults: FuzzySearchResult[] = []; - - if (isSearchWildCardAll) { - searchResults = componentNames.map(name => ({ matchType: 'all', distance: 0, item: name } as FuzzySearchResult)); - } else { - searchResults = fuzzySearch(searchQuery, names, { - maxDistance: 3, - maxResults: 10, - isFuzzyMatch: true, - deduplicateByNormalized: true - }); - } - - const extendResults = (results: FuzzySearchResult[] = []) => results.map(result => { - const isSchemasAvailable = pfComponentNames.includes(result.item); - const urls = componentToDocsMap.get(result.item) || []; - const matchedUrls = new Set(); - - urls.forEach(url => { - matchedUrls.add(url); - }); - - return { - ...result, - doc: `patternfly://docs/${result.item}`, - isSchemasAvailable, - schema: isSchemasAvailable ? `patternfly://schemas/${result.item}` : undefined, - urls: Array.from(matchedUrls) - }; - }); - - const exactMatches = searchResults.filter(result => result.matchType === 'exact'); - const extendedExactMatches = extendResults(exactMatches); - const extendedSearchResults = extendResults(searchResults); - - return { - isSearchWildCardAll, - firstExactMatch: extendedExactMatches[0], - exactMatches: extendedExactMatches, - searchResults: extendedSearchResults - }; -}; - -/** - * Memoized version of searchComponents. - */ -searchComponents.memo = memo(searchComponents, DEFAULT_OPTIONS.toolMemoOptions.searchPatternFlyDocs); +import { getOptions } from './options.context'; +import { searchPatternFly } from './patternFly.search'; +import { getPatternFlyMcpResources } from './patternFly.getResources'; +// import { getPatternFlyMcpDocs } from './patternFly.getResources'; /** * searchPatternFlyDocs tool function @@ -216,36 +34,102 @@ const searchPatternFlyDocsTool = (options = getOptions()): McpTool => { ); } - const { isSearchWildCardAll, searchResults } = searchComponents.memo(searchQuery, { allowWildCardAll: true }); + const { isSearchWildCardAll, searchResults } = await searchPatternFly.memo(searchQuery, { allowWildCardAll: true }); + // const { envVersion } = await getPatternFlyMcpResources.memo(); if (!isSearchWildCardAll && searchResults.length === 0) { return { content: [{ type: 'text', text: stringJoin.newline( - `No PatternFly documentation found matching "${searchQuery}"`, + `No PatternFly resources found matching "${searchQuery}"`, '', '---', + // '', + // '**Environment snapshot**:', + // ` - Detected PatternFly Version: ${closestVersion}`, + // '', + '---', '', '**Important**:', - ' - Use a search all ("*") to find all available components.' + ' - Use a search all ("*") to find all available resources.' ) }] }; } + const { envVersion } = await getPatternFlyMcpResources.memo(); const results = searchResults.map(result => { - const urlList = result.urls.map((url: string, index: number) => ` ${index + 1}. ${url}`).join('\n'); + /* + const urlList = result.entriesNoGuidance.length ? stringJoin.newline( + ...Object.entries(result.versions) + .sort(([a], [b]) => b.localeCompare(a)) + .map(([version, entries]) => + stringJoin.newline( + `### PatternFly ${version} documentation URLs`, + ...entries.entriesNoGuidance.map((entry, index) => ` ${index + 1}. ${entry.path}`) + )) + ) : ' - No documentation URLs found'; + */ + + let urlList = ' - No documentation URLs found'; + + if (result.entriesNoGuidance?.length) { + urlList = stringJoin.newline( + ...Object.entries(result.versions) + .sort(([a], [b]) => b.localeCompare(a)) + .map(([version, entries]) => + stringJoin.newline( + `#### Documentation URLs ${version}`, + ...entries.entriesNoGuidance + .sort((a, b) => a.displayName.localeCompare(b.displayName)) + .map((entry, index) => ` ${index + 1}. [${entry.displayName} - ${entry.displayCategory} (${entry.version})](${entry.path})`) + // .map((entry, index) => ` ${index + 1}. ${entry.path}`) + )) + ); + } + + // ...result.entriesNoGuidance.map((entry, index) => ` ${index + 1}. ${entry.path}`) + // const isDetectedVersion = entry.version === closestVersion; + // return ` ${index + 1}. ${entry.path}${isDetectedVersion ? ' **[Detected version]**' : ''}`; + + /* + const guidanceUrlList = result.entriesGuidance.length + ? stringJoin.newline( + ...result.entriesGuidance.map((entry, index) => ` ${index + 1}. ${entry.path}`) + // const isDetectedVersion = entry.version === closestVersion; + // return ` ${index + 1}. ${entry.path}${isDetectedVersion ? ' **[Detected version]**' : ''}`; + ) + : ' - No guidance URLs found'; + */ + + let guidanceUrlList = ' - No guidance URLs found'; + + if (result.entriesGuidance?.length) { + guidanceUrlList = stringJoin.newline( + ...Object.entries(result.versions) + .sort(([a], [b]) => b.localeCompare(a)) + .map(([version, resultEntry]) => + stringJoin.newline( + `#### AI guidance URLs ${version}`, + ...resultEntry.entriesGuidance + .sort((a, b) => a.displayName.localeCompare(b.displayName)) + .map((entry, index) => ` ${index + 1}. [${entry.displayName} - ${entry.displayCategory} (${entry.version})](${entry.path})`) + // .map((entry, index) => ` ${index + 1}. ${entry.path}`) + )) + ); + } return stringJoin.newline( '', `## ${result.item}`, `**Match Type**: ${result.matchType}`, - `### "usePatternFlyDocs" tool documentation URLs`, - urlList.length ? urlList : ' - No URLs found', + `### "usePatternFlyDocs" tool resource URLs`, + urlList, + guidanceUrlList, `### Resources metadata`, ` - **Component name**: ${result.item}`, - ` - **JSON Schemas**: ${result.isSchemasAvailable ? 'Available' : 'Not available'}` + ` - **JSON Schemas**: ${result.versions?.[envVersion]?.isSchemasAvailable ? 'Available' : 'Not available'}` ); }); @@ -253,14 +137,19 @@ const searchPatternFlyDocsTool = (options = getOptions()): McpTool => { content: [{ type: 'text', text: stringJoin.newline( - `# Search results for "${isSearchWildCardAll ? 'all components' : searchQuery}", ${searchResults.length} matches found:`, + `# Search results for "${isSearchWildCardAll ? 'all resources' : searchQuery}", ${searchResults.length} matches found:`, ...results, '', '---', + // '', + // '**Environment snapshot**:', + // ` - Detected PatternFly Version: ${closestVersion}`, + '', + '---', '', '**Important**:', - ' - Use the "usePatternFlyDocs" tool with the above URLs to fetch documentation content.', - ' - Use a search all ("*") to find all available components.' + ' - Use the "usePatternFlyDocs" tool with the above URLs to fetch resource content.', + ' - Use a search all ("*") to find all available resources.' ) }] }; @@ -269,18 +158,18 @@ const searchPatternFlyDocsTool = (options = getOptions()): McpTool => { return [ 'searchPatternFlyDocs', { - description: `Search PatternFly components and get component names with documentation URLs. Supports case-insensitive partial and all ("*") matches. + description: `Search PatternFly resources and get component names with documentation and guidance URLs. Supports case-insensitive partial and all ("*") matches. **Usage**: - 1. Input a "searchQuery" to find PatternFly documentation URLs and component names. - 2. Use the returned component names OR URLs with the "usePatternFlyDocs" tool to get markdown documentation and component JSON schemas. + 1. Input a "searchQuery" to find PatternFly documentation and guideline URLs, and component names. + 2. Use the returned resource names OR URLs OR version with the "usePatternFlyDocs" tool to get markdown documentation, guidelines, and component JSON schemas. **Returns**: - - Component names that can be used with "usePatternFlyDocs" - - Documentation URLs that can be used with "usePatternFlyDocs" + - Component and resource names that can be used with "usePatternFlyDocs" + - Documentation and guideline URLs that can be used with "usePatternFlyDocs" `, inputSchema: { - searchQuery: z.string().max(options.maxSearchLength).describe('Full or partial component name to search for (e.g., "button", "table", "*")') + searchQuery: z.string().max(options.maxSearchLength).describe('Full or partial resource or component name to search for (e.g., "button", "react", "*")') } }, callback @@ -289,4 +178,4 @@ const searchPatternFlyDocsTool = (options = getOptions()): McpTool => { searchPatternFlyDocsTool.toolName = 'searchPatternFlyDocs'; -export { searchPatternFlyDocsTool, searchComponents, setComponentToDocsMap, componentNames }; +export { searchPatternFlyDocsTool }; diff --git a/tests/e2e/httpTransport.test.ts b/tests/e2e/httpTransport.test.ts index f1eec23c..6d6e824e 100644 --- a/tests/e2e/httpTransport.test.ts +++ b/tests/e2e/httpTransport.test.ts @@ -186,7 +186,8 @@ describe('Builtin resources, HTTP transport', () => { const updatedTemplates = templates?.result?.resourceTemplates || []; const templateNames = updatedTemplates.map((template: any) => template.uriTemplate).sort(); - expect({ resourceNames, templateNames }).toMatchSnapshot('resources'); + expect(resourceNames).toContain('patternfly://context'); + expect(templateNames).toContain('patternfly://components/index{?version,section,category}'); }); it('should read the patternfly-context resource', async () => { diff --git a/tests/e2e/stdioTransport.test.ts b/tests/e2e/stdioTransport.test.ts index 424ad06b..1463c04a 100644 --- a/tests/e2e/stdioTransport.test.ts +++ b/tests/e2e/stdioTransport.test.ts @@ -184,7 +184,8 @@ describe('Builtin resources, STDIO', () => { const updatedTemplates = templates?.result?.resourceTemplates || []; const templateNames = updatedTemplates.map((template: any) => template.uriTemplate).sort(); - expect({ resourceNames, templateNames }).toMatchSnapshot(); + expect(resourceNames).toContain('patternfly://context'); + expect(templateNames).toContain('patternfly://components/index{?version,section,category}'); }); it('should read the patternfly-context resource', async () => {