From b9d74126624409e5951c04deac8c595662e31274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Gra=CC=88=C3=9Fl?= Date: Mon, 8 Sep 2025 17:48:34 +0200 Subject: [PATCH] docs: Add stories using GraphQL --- package-lock.json | 25 ++++ package.json | 2 + .../TableToolsTable.stories.js | 128 +++++++++++++++++- src/support/api/backend/index.js | 22 +++ src/support/api/graphql/index.js | 17 +++ src/support/{api.js => api/rest/handlers.js} | 3 +- src/support/api/rest/helpers.js | 0 .../{mswHandler.js => api/rest/index.js} | 2 +- src/support/defaultStoryMeta.js | 4 +- 9 files changed, 196 insertions(+), 7 deletions(-) create mode 100644 src/support/api/backend/index.js create mode 100644 src/support/api/graphql/index.js rename src/support/{api.js => api/rest/handlers.js} (93%) create mode 100644 src/support/api/rest/helpers.js rename src/support/{mswHandler.js => api/rest/index.js} (97%) diff --git a/package-lock.json b/package-lock.json index a29880c..2532f2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,8 @@ "eslint-plugin-storybook": "^9.1.4", "eslint-plugin-testing-library": "^7.6.8", "fishery": "^2.3.1", + "graphql": "^16.11.0", + "graphql-request": "^7.2.0", "identity-obj-proxy": "^3.0.0", "jest": "^30.1.3", "jest-environment-jsdom": "^30.1.2", @@ -1920,6 +1922,16 @@ "@shikijs/vscode-textmate": "^10.0.2" } }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -10439,6 +10451,19 @@ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, + "node_modules/graphql-request": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-7.2.0.tgz", + "integrity": "sha512-0GR7eQHBFYz372u9lxS16cOtEekFlZYB2qOyq8wDvzRmdRSJ0mgUVX1tzNcIzk3G+4NY+mGtSz411wZdeDF/+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0" + }, + "peerDependencies": { + "graphql": "14 - 16" + } + }, "node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", diff --git a/package.json b/package.json index 40ee644..faabffc 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,8 @@ "eslint-plugin-storybook": "^9.1.4", "eslint-plugin-testing-library": "^7.6.8", "fishery": "^2.3.1", + "graphql": "^16.11.0", + "graphql-request": "^7.2.0", "identity-obj-proxy": "^3.0.0", "jest": "^30.1.3", "jest-environment-jsdom": "^30.1.2", diff --git a/src/components/TableToolsTable/TableToolsTable.stories.js b/src/components/TableToolsTable/TableToolsTable.stories.js index 53179a4..34ca8c3 100644 --- a/src/components/TableToolsTable/TableToolsTable.stories.js +++ b/src/components/TableToolsTable/TableToolsTable.stories.js @@ -1,8 +1,12 @@ import React, { useCallback, useEffect } from 'react'; import propTypes from 'prop-types'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { gql, request } from 'graphql-request'; import defaultStoryMeta from '~/support/defaultStoryMeta'; +import mswGraphQlHandlers from '~/support/api/graphql'; +import mswRestHandlers from '~/support/api/rest'; + import columns from '~/support/factories/columns'; import filters, { customNumberFilterType, @@ -16,10 +20,12 @@ import CustomEmptyState from '~/support/components/CustomEmptyState'; import DetailsRow from '~/support/components/DetailsRow'; import DedicatedAction from '~/support/components/DedicatedAction'; import { actions, rowActionResolver } from '~/support/constants'; -import { selectedItemIds } from '~/support/api'; - +// TODO fix preselection +// import { selectedItemIds } from '~/support/api'; +const selectedItemIds = []; import { TableToolsTable, TableStateProvider } from '~/components'; import { useFullTableState, useStateCallbacks } from '~/hooks'; +import { useQueryWithUtilities } from '~/utilities'; const queryClient = new QueryClient(); @@ -208,6 +214,124 @@ export const Common = { ], render: (args) => , }; +const GET_TRACKS_QUERY = gql` + query GetTracks($sort: String) { + tracks(sort: $sort) { + id + } + } +`; + +const GraphQLExample = ({ + debug, + columns, + filters, + filtered, + enableDefaultFilter, + defaultFilter, + sortable, + initialSort, + enableInitialSort, + manageColumns, + enableRowActions, + enableActions, + dedicatedAction, + customEmptyRows, + customEmptyState, + enableExport, + enableDetails, + enableBulkSelect, + enablePreselection, + enableSimpleBulkSelect, +}) => { + const fetchFn = useCallback(async (params) => { + console.log('params gql', params); + return await request('http://local.com/graphql', GET_TRACKS_QUERY, params); + }, []); + + const { + loading, + result: { data, meta: { total } = {} } = {}, + error, + exporter, + itemIdsInTable, + itemIdsOnPage, + } = useQueryWithUtilities({ + fetchFn, + useTableState: true, + }); + + return ( + ({ ...column, sortable: undefined })) + } + {...(filters && filtered + ? { + filters: { + filterConfig: [...filters, customNumberFilter], + customFilterTypes: { + number: customNumberFilterType, + }, + ...(enableDefaultFilter ? { activeFilters: defaultFilter } : {}), + }, + } + : {})} + options={{ + ...defaultOptions, + debug, + manageColumns, + ...(enableInitialSort ? { sortBy: initialSort } : {}), + ...(enableRowActions + ? { + actionResolver: rowActionResolver, + } + : {}), + ...(enableActions ? { actions } : {}), + ...(dedicatedAction ? { dedicatedAction: DedicatedAction } : {}), + ...(customEmptyRows ? { emptyRows: emptyRows(columns?.length) } : {}), + ...(customEmptyState ? { EmptyState: CustomEmptyState } : {}), + ...(enableExport ? { exporter } : {}), + ...(enableDetails ? { detailsComponent: DetailsRow } : {}), + ...(enableBulkSelect + ? { + ...(enablePreselection ? { selected: selectedItemIds } : {}), + onSelect, + itemIdsInTable, + itemIdsOnPage, + } + : {}), + ...(enableSimpleBulkSelect ? { onSelect: true } : {}), + }} + /> + ); +}; + +GraphQLExample.propTypes = argProps; + +export const GraphQL = { + parameters: { + msw: { + handlers: [...mswGraphQlHandlers, ...mswRestHandlers], + }, + }, + decorators: [ + (Story) => ( + + + + + + ), + ], + render: (args) => , +}; const WithTableTreeExample = ({ debug, diff --git a/src/support/api/backend/index.js b/src/support/api/backend/index.js new file mode 100644 index 0000000..23c4297 --- /dev/null +++ b/src/support/api/backend/index.js @@ -0,0 +1,22 @@ +const { DatabaseSync } = require('node:sqlite'); +const database = new DatabaseSync(':memory:'); + +const initialise = () => { + database.exec(` + CREATE TABLE data( + key INTEGER PRIMARY KEY, + value TEXT + ) STRICT + `); +}; + +const query = (sql) => { + const dbQuery = database.prepare('SELECT * FROM data ORDER BY key'); + + console.log(dbQuery.all()); +}; + +export default { + initialise, + query, +}; diff --git a/src/support/api/graphql/index.js b/src/support/api/graphql/index.js new file mode 100644 index 0000000..3ddc701 --- /dev/null +++ b/src/support/api/graphql/index.js @@ -0,0 +1,17 @@ +import { graphql, HttpResponse } from 'msw'; + +const api = graphql.link('http://local.com/graphql'); + +export default [ + api.query('GetTracks', ({ variables, ...rest }) => { + console.log('gql rest', variables, rest); + return HttpResponse.json({ + data: { + user: { + id: variables.id, + name: 'John Maverick', + }, + }, + }); + }), +]; diff --git a/src/support/api.js b/src/support/api/rest/handlers.js similarity index 93% rename from src/support/api.js rename to src/support/api/rest/handlers.js index efe1209..49d75dc 100644 --- a/src/support/api.js +++ b/src/support/api/rest/handlers.js @@ -36,10 +36,9 @@ const queriedItems = (itemsToQuery) => { const totalItems = itemsToQuery.slice(0, total); const query = buildQuery(filters, sort); const items = query.length ? jsonquery(totalItems, query) : totalItems; - const actualLimit = limit === 'max' ? items.length : limit; const data = items.slice( parseInt(offset), - parseInt(offset) + parseInt(actualLimit), + parseInt(offset) + parseInt(limit), ); return { diff --git a/src/support/api/rest/helpers.js b/src/support/api/rest/helpers.js new file mode 100644 index 0000000..e69de29 diff --git a/src/support/mswHandler.js b/src/support/api/rest/index.js similarity index 97% rename from src/support/mswHandler.js rename to src/support/api/rest/index.js index 54de8eb..a3f7c5c 100644 --- a/src/support/mswHandler.js +++ b/src/support/api/rest/index.js @@ -5,7 +5,7 @@ import { apiTreehandler, apiGenresHandler, apiSelectionHandler, -} from './api'; +} from './handlers'; const DEFAULT_DELAY = 500; diff --git a/src/support/defaultStoryMeta.js b/src/support/defaultStoryMeta.js index d0f3fdf..150f576 100644 --- a/src/support/defaultStoryMeta.js +++ b/src/support/defaultStoryMeta.js @@ -7,12 +7,12 @@ import { PanelMainBody, } from '@patternfly/react-core'; -import mswHandlers from './mswHandler'; +import mswRestHandlers from './api/rest'; const meta = { parameters: { msw: { - handlers: mswHandlers, + handlers: mswRestHandlers, }, }, decorators: [