@wilshop/dashboard 3.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -0
- package/dist/plugin/api/api-extensions.d.ts +1 -0
- package/dist/plugin/api/api-extensions.js +35 -0
- package/dist/plugin/api/metrics.resolver.d.ts +8 -0
- package/dist/plugin/api/metrics.resolver.js +40 -0
- package/dist/plugin/config/metrics-strategies.d.ts +39 -0
- package/dist/plugin/config/metrics-strategies.js +74 -0
- package/dist/plugin/constants.d.ts +8 -0
- package/dist/plugin/constants.js +31 -0
- package/dist/plugin/dashboard.plugin.d.ts +115 -0
- package/dist/plugin/dashboard.plugin.js +339 -0
- package/dist/plugin/default-page.html +193 -0
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/index.js +18 -0
- package/dist/plugin/package.json +3 -0
- package/dist/plugin/service/metrics.service.d.ts +15 -0
- package/dist/plugin/service/metrics.service.js +129 -0
- package/dist/plugin/types.d.ts +20 -0
- package/dist/plugin/types.js +9 -0
- package/dist/vite/constants.d.ts +5 -0
- package/dist/vite/constants.js +278 -0
- package/dist/vite/index.d.ts +1 -0
- package/dist/vite/index.js +1 -0
- package/dist/vite/types.d.ts +79 -0
- package/dist/vite/types.js +1 -0
- package/dist/vite/utils/ast-utils.d.ts +5 -0
- package/dist/vite/utils/ast-utils.js +29 -0
- package/dist/vite/utils/ast-utils.spec.d.ts +1 -0
- package/dist/vite/utils/ast-utils.spec.js +45 -0
- package/dist/vite/utils/compiler.d.ts +23 -0
- package/dist/vite/utils/compiler.js +165 -0
- package/dist/vite/utils/get-dashboard-paths.d.ts +5 -0
- package/dist/vite/utils/get-dashboard-paths.js +20 -0
- package/dist/vite/utils/logger.d.ts +3 -0
- package/dist/vite/utils/logger.js +39 -0
- package/dist/vite/utils/plugin-discovery.d.ts +27 -0
- package/dist/vite/utils/plugin-discovery.js +398 -0
- package/dist/vite/utils/schema-generator.d.ts +10 -0
- package/dist/vite/utils/schema-generator.js +35 -0
- package/dist/vite/utils/tsconfig-utils.d.ts +9 -0
- package/dist/vite/utils/tsconfig-utils.js +51 -0
- package/dist/vite/utils/ui-config.d.ts +3 -0
- package/dist/vite/utils/ui-config.js +30 -0
- package/dist/vite/vite-plugin-admin-api-schema.d.ts +24 -0
- package/dist/vite/vite-plugin-admin-api-schema.js +82 -0
- package/dist/vite/vite-plugin-config-loader.d.ts +16 -0
- package/dist/vite/vite-plugin-config-loader.js +70 -0
- package/dist/vite/vite-plugin-config.d.ts +4 -0
- package/dist/vite/vite-plugin-config.js +60 -0
- package/dist/vite/vite-plugin-dashboard-metadata.d.ts +7 -0
- package/dist/vite/vite-plugin-dashboard-metadata.js +61 -0
- package/dist/vite/vite-plugin-gql-tada.d.ts +6 -0
- package/dist/vite/vite-plugin-gql-tada.js +51 -0
- package/dist/vite/vite-plugin-hmr.d.ts +8 -0
- package/dist/vite/vite-plugin-hmr.js +34 -0
- package/dist/vite/vite-plugin-lingui-babel.d.ts +27 -0
- package/dist/vite/vite-plugin-lingui-babel.js +86 -0
- package/dist/vite/vite-plugin-tailwind-source.d.ts +7 -0
- package/dist/vite/vite-plugin-tailwind-source.js +48 -0
- package/dist/vite/vite-plugin-theme.d.ts +55 -0
- package/dist/vite/vite-plugin-theme.js +130 -0
- package/dist/vite/vite-plugin-transform-index.d.ts +11 -0
- package/dist/vite/vite-plugin-transform-index.js +40 -0
- package/dist/vite/vite-plugin-translations.d.ts +31 -0
- package/dist/vite/vite-plugin-translations.js +177 -0
- package/dist/vite/vite-plugin-ui-config.d.ts +150 -0
- package/dist/vite/vite-plugin-ui-config.js +36 -0
- package/dist/vite/vite-plugin-vendure-dashboard.d.ts +142 -0
- package/dist/vite/vite-plugin-vendure-dashboard.js +170 -0
- package/index.html +14 -0
- package/lingui.config.js +37 -0
- package/package.json +178 -0
- package/src/app/app-providers.tsx +31 -0
- package/src/app/common/delete-bulk-action.tsx +149 -0
- package/src/app/common/duplicate-bulk-action.tsx +147 -0
- package/src/app/common/duplicate-entity-dialog.tsx +117 -0
- package/src/app/common/map-faceted-filter-fields.ts +21 -0
- package/src/app/common/set-document-direction.ts +7 -0
- package/src/app/common/use-page-title.test.ts +263 -0
- package/src/app/common/use-page-title.ts +86 -0
- package/src/app/main.tsx +134 -0
- package/src/app/routeTree.gen.ts +1284 -0
- package/src/app/routes/__root.tsx +23 -0
- package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +88 -0
- package/src/app/routes/_authenticated/_administrators/administrators.tsx +104 -0
- package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +160 -0
- package/src/app/routes/_authenticated/_administrators/components/administrator-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +133 -0
- package/src/app/routes/_authenticated/_assets/assets.graphql.ts +76 -0
- package/src/app/routes/_authenticated/_assets/assets.tsx +48 -0
- package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +181 -0
- package/src/app/routes/_authenticated/_assets/components/asset-bulk-actions.tsx +47 -0
- package/src/app/routes/_authenticated/_assets/components/asset-tag-filter.tsx +206 -0
- package/src/app/routes/_authenticated/_assets/components/asset-tags-editor.tsx +226 -0
- package/src/app/routes/_authenticated/_assets/components/manage-tags-dialog.tsx +212 -0
- package/src/app/routes/_authenticated/_channels/channels.graphql.ts +102 -0
- package/src/app/routes/_authenticated/_channels/channels.tsx +81 -0
- package/src/app/routes/_authenticated/_channels/channels_.$id.tsx +251 -0
- package/src/app/routes/_authenticated/_channels/components/channel-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_collections/collections.graphql.ts +193 -0
- package/src/app/routes/_authenticated/_collections/collections.tsx +337 -0
- package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +236 -0
- package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +130 -0
- package/src/app/routes/_authenticated/_collections/components/collection-contents-preview-table.tsx +153 -0
- package/src/app/routes/_authenticated/_collections/components/collection-contents-sheet.tsx +50 -0
- package/src/app/routes/_authenticated/_collections/components/collection-contents-table.tsx +83 -0
- package/src/app/routes/_authenticated/_collections/components/collection-filters-selector.tsx +24 -0
- package/src/app/routes/_authenticated/_collections/components/move-collections-dialog.tsx +433 -0
- package/src/app/routes/_authenticated/_countries/components/country-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_countries/countries.graphql.ts +82 -0
- package/src/app/routes/_authenticated/_countries/countries.tsx +72 -0
- package/src/app/routes/_authenticated/_countries/countries_.$id.tsx +123 -0
- package/src/app/routes/_authenticated/_customer-groups/components/customer-group-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_customer-groups/components/customer-group-members-sheet.tsx +51 -0
- package/src/app/routes/_authenticated/_customer-groups/components/customer-group-members-table.tsx +134 -0
- package/src/app/routes/_authenticated/_customer-groups/customer-groups.graphql.ts +80 -0
- package/src/app/routes/_authenticated/_customer-groups/customer-groups.tsx +72 -0
- package/src/app/routes/_authenticated/_customer-groups/customer-groups_.$id.tsx +121 -0
- package/src/app/routes/_authenticated/_customers/components/customer-address-card.tsx +155 -0
- package/src/app/routes/_authenticated/_customers/components/customer-address-form.tsx +347 -0
- package/src/app/routes/_authenticated/_customers/components/customer-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_customers/components/customer-group-controls.tsx +4 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-container.tsx +78 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-types.ts +5 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-utils.tsx +124 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history.tsx +113 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/default-customer-history-components.tsx +176 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/index.ts +4 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/use-customer-history.ts +169 -0
- package/src/app/routes/_authenticated/_customers/components/customer-order-table.tsx +90 -0
- package/src/app/routes/_authenticated/_customers/components/customer-status-badge.tsx +33 -0
- package/src/app/routes/_authenticated/_customers/customers.graphql.ts +218 -0
- package/src/app/routes/_authenticated/_customers/customers.tsx +104 -0
- package/src/app/routes/_authenticated/_customers/customers_.$id.tsx +280 -0
- package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +99 -0
- package/src/app/routes/_authenticated/_facets/components/facet-value-bulk-actions.tsx +16 -0
- package/src/app/routes/_authenticated/_facets/components/facet-values-sheet.tsx +49 -0
- package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +139 -0
- package/src/app/routes/_authenticated/_facets/facets.graphql.ts +182 -0
- package/src/app/routes/_authenticated/_facets/facets.tsx +121 -0
- package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +158 -0
- package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +152 -0
- package/src/app/routes/_authenticated/_global-settings/global-settings.graphql.ts +28 -0
- package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +161 -0
- package/src/app/routes/_authenticated/_orders/components/add-manual-payment-dialog.tsx +189 -0
- package/src/app/routes/_authenticated/_orders/components/add-surcharge-form.tsx +139 -0
- package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +100 -0
- package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +287 -0
- package/src/app/routes/_authenticated/_orders/components/fulfill-order-dialog.tsx +320 -0
- package/src/app/routes/_authenticated/_orders/components/fulfillment-details.tsx +159 -0
- package/src/app/routes/_authenticated/_orders/components/money-gross-net.tsx +20 -0
- package/src/app/routes/_authenticated/_orders/components/order-address.tsx +65 -0
- package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +307 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/default-order-history-components.tsx +98 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/index.ts +3 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/order-history-container.tsx +80 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/order-history-types.ts +5 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/order-history-utils.tsx +173 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/order-history.tsx +104 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/use-order-history.ts +175 -0
- package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +53 -0
- package/src/app/routes/_authenticated/_orders/components/order-modification-preview-dialog.tsx +365 -0
- package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +261 -0
- package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +100 -0
- package/src/app/routes/_authenticated/_orders/components/order-table.tsx +194 -0
- package/src/app/routes/_authenticated/_orders/components/order-tax-summary.tsx +40 -0
- package/src/app/routes/_authenticated/_orders/components/payment-details.tsx +293 -0
- package/src/app/routes/_authenticated/_orders/components/seller-orders-card.tsx +63 -0
- package/src/app/routes/_authenticated/_orders/components/settle-refund-dialog.tsx +80 -0
- package/src/app/routes/_authenticated/_orders/components/shipping-method-selector.tsx +64 -0
- package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +99 -0
- package/src/app/routes/_authenticated/_orders/components/use-transition-order-to-state.tsx +151 -0
- package/src/app/routes/_authenticated/_orders/orders.graphql.ts +775 -0
- package/src/app/routes/_authenticated/_orders/orders.tsx +120 -0
- package/src/app/routes/_authenticated/_orders/orders_.$aggregateOrderId_.seller-orders.$sellerOrderId.tsx +50 -0
- package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +30 -0
- package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +298 -0
- package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +495 -0
- package/src/app/routes/_authenticated/_orders/utils/order-detail-loaders.tsx +134 -0
- package/src/app/routes/_authenticated/_orders/utils/order-types.ts +10 -0
- package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +159 -0
- package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +335 -0
- package/src/app/routes/_authenticated/_payment-methods/components/payment-eligibility-checker-selector.tsx +37 -0
- package/src/app/routes/_authenticated/_payment-methods/components/payment-handler-selector.tsx +33 -0
- package/src/app/routes/_authenticated/_payment-methods/components/payment-method-bulk-actions.tsx +58 -0
- package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +110 -0
- package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +88 -0
- package/src/app/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx +197 -0
- package/src/app/routes/_authenticated/_product-variants/components/add-currency-dropdown.tsx +49 -0
- package/src/app/routes/_authenticated/_product-variants/components/add-stock-location-dropdown.tsx +56 -0
- package/src/app/routes/_authenticated/_product-variants/components/product-variant-bulk-actions.tsx +110 -0
- package/src/app/routes/_authenticated/_product-variants/components/variant-price-detail.tsx +88 -0
- package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +207 -0
- package/src/app/routes/_authenticated/_product-variants/product-variants.tsx +98 -0
- package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +471 -0
- package/src/app/routes/_authenticated/_products/components/add-option-group-dialog.tsx +127 -0
- package/src/app/routes/_authenticated/_products/components/add-product-variant-dialog.tsx +372 -0
- package/src/app/routes/_authenticated/_products/components/assign-facet-values-dialog.tsx +282 -0
- package/src/app/routes/_authenticated/_products/components/create-product-options-dialog.tsx +416 -0
- package/src/app/routes/_authenticated/_products/components/create-product-variants-dialog.tsx +190 -0
- package/src/app/routes/_authenticated/_products/components/create-product-variants.tsx +370 -0
- package/src/app/routes/_authenticated/_products/components/option-groups-editor.tsx +180 -0
- package/src/app/routes/_authenticated/_products/components/option-value-input.tsx +72 -0
- package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +117 -0
- package/src/app/routes/_authenticated/_products/components/product-option-group-badge.tsx +19 -0
- package/src/app/routes/_authenticated/_products/components/product-option-select.tsx +111 -0
- package/src/app/routes/_authenticated/_products/components/product-options-table.tsx +114 -0
- package/src/app/routes/_authenticated/_products/components/product-variants-table.tsx +120 -0
- package/src/app/routes/_authenticated/_products/product-option-groups.graphql.ts +103 -0
- package/src/app/routes/_authenticated/_products/products.graphql.ts +352 -0
- package/src/app/routes/_authenticated/_products/products.tsx +121 -0
- package/src/app/routes/_authenticated/_products/products_.$id.tsx +258 -0
- package/src/app/routes/_authenticated/_products/products_.$id_.variants.tsx +405 -0
- package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$id.tsx +177 -0
- package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +204 -0
- package/src/app/routes/_authenticated/_profile/profile.graphql.ts +23 -0
- package/src/app/routes/_authenticated/_profile/profile.tsx +119 -0
- package/src/app/routes/_authenticated/_promotions/components/promotion-actions-selector.tsx +35 -0
- package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +72 -0
- package/src/app/routes/_authenticated/_promotions/components/promotion-conditions-selector.tsx +35 -0
- package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +121 -0
- package/src/app/routes/_authenticated/_promotions/promotions.tsx +94 -0
- package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +245 -0
- package/src/app/routes/_authenticated/_roles/components/expandable-permissions.tsx +54 -0
- package/src/app/routes/_authenticated/_roles/components/permissions-table-grid.tsx +251 -0
- package/src/app/routes/_authenticated/_roles/components/role-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_roles/roles.graphql.ts +76 -0
- package/src/app/routes/_authenticated/_roles/roles.tsx +99 -0
- package/src/app/routes/_authenticated/_roles/roles_.$id.tsx +146 -0
- package/src/app/routes/_authenticated/_sellers/components/seller-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +70 -0
- package/src/app/routes/_authenticated/_sellers/sellers.tsx +56 -0
- package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +112 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/fulfillment-handler-selector.tsx +56 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/metadata-badges.tsx +15 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/price-display.tsx +21 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/shipping-calculator-selector.tsx +33 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/shipping-eligibility-checker-selector.tsx +36 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/shipping-method-bulk-actions.tsx +61 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/shipping-method-test-result-wrapper.tsx +87 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/test-address-form.tsx +256 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/test-order-builder.tsx +244 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-methods-result.tsx +97 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-methods-sheet.tsx +41 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/test-shipping-methods.tsx +74 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/test-single-method-result.tsx +90 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/test-single-shipping-method-sheet.tsx +56 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/test-single-shipping-method.tsx +82 -0
- package/src/app/routes/_authenticated/_shipping-methods/components/use-shipping-method-test-state.ts +67 -0
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +137 -0
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +76 -0
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +197 -0
- package/src/app/routes/_authenticated/_stock-locations/components/stock-location-bulk-actions.tsx +58 -0
- package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +87 -0
- package/src/app/routes/_authenticated/_stock-locations/stock-locations.tsx +65 -0
- package/src/app/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx +126 -0
- package/src/app/routes/_authenticated/_system/components/payload-dialog.tsx +41 -0
- package/src/app/routes/_authenticated/_system/healthchecks.tsx +99 -0
- package/src/app/routes/_authenticated/_system/job-queue.graphql.ts +54 -0
- package/src/app/routes/_authenticated/_system/job-queue.tsx +265 -0
- package/src/app/routes/_authenticated/_system/scheduled-tasks.tsx +249 -0
- package/src/app/routes/_authenticated/_tax-categories/components/tax-category-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +72 -0
- package/src/app/routes/_authenticated/_tax-categories/tax-categories.tsx +69 -0
- package/src/app/routes/_authenticated/_tax-categories/tax-categories_.$id.tsx +127 -0
- package/src/app/routes/_authenticated/_tax-rates/components/tax-rate-bulk-actions.tsx +15 -0
- package/src/app/routes/_authenticated/_tax-rates/tax-rates.graphql.ts +84 -0
- package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +119 -0
- package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +149 -0
- package/src/app/routes/_authenticated/_zones/components/zone-bulk-actions.tsx +63 -0
- package/src/app/routes/_authenticated/_zones/components/zone-countries-add.tsx +0 -0
- package/src/app/routes/_authenticated/_zones/components/zone-countries-sheet.tsx +34 -0
- package/src/app/routes/_authenticated/_zones/components/zone-countries-table.tsx +94 -0
- package/src/app/routes/_authenticated/_zones/zones.graphql.ts +105 -0
- package/src/app/routes/_authenticated/_zones/zones.tsx +64 -0
- package/src/app/routes/_authenticated/_zones/zones_.$id.tsx +110 -0
- package/src/app/routes/_authenticated/index.tsx +233 -0
- package/src/app/routes/_authenticated.tsx +35 -0
- package/src/app/routes/login.tsx +49 -0
- package/src/app/styles.css +100 -0
- package/src/app/tailwindcss-animate.css +275 -0
- package/src/i18n/common-strings.ts +111 -0
- package/src/i18n/locales/ar.po +4972 -0
- package/src/i18n/locales/bg.po +3436 -0
- package/src/i18n/locales/cs.po +4972 -0
- package/src/i18n/locales/de.po +4972 -0
- package/src/i18n/locales/en.po +4972 -0
- package/src/i18n/locales/es.po +4972 -0
- package/src/i18n/locales/fa.po +4972 -0
- package/src/i18n/locales/fr.po +4972 -0
- package/src/i18n/locales/he.po +4972 -0
- package/src/i18n/locales/hr.po +4972 -0
- package/src/i18n/locales/it.po +4972 -0
- package/src/i18n/locales/ja.po +4972 -0
- package/src/i18n/locales/ko.po +4628 -0
- package/src/i18n/locales/nb.po +4972 -0
- package/src/i18n/locales/ne.po +4972 -0
- package/src/i18n/locales/nl.po +4628 -0
- package/src/i18n/locales/pl.po +4972 -0
- package/src/i18n/locales/pt_BR.po +4972 -0
- package/src/i18n/locales/pt_PT.po +4972 -0
- package/src/i18n/locales/ru.po +4972 -0
- package/src/i18n/locales/sv.po +4972 -0
- package/src/i18n/locales/tr.po +4972 -0
- package/src/i18n/locales/uk.po +4972 -0
- package/src/i18n/locales/zh_Hans.po +4972 -0
- package/src/i18n/locales/zh_Hant.po +4972 -0
- package/src/lib/components/data-display/boolean.tsx +23 -0
- package/src/lib/components/data-display/date-time.tsx +19 -0
- package/src/lib/components/data-display/json.tsx +20 -0
- package/src/lib/components/data-display/money.tsx +14 -0
- package/src/lib/components/data-input/affixed-input.stories.tsx +93 -0
- package/src/lib/components/data-input/affixed-input.tsx +79 -0
- package/src/lib/components/data-input/boolean-input.stories.tsx +102 -0
- package/src/lib/components/data-input/boolean-input.tsx +16 -0
- package/src/lib/components/data-input/checkbox-input.stories.tsx +61 -0
- package/src/lib/components/data-input/checkbox-input.tsx +15 -0
- package/src/lib/components/data-input/combination-mode-input.tsx +61 -0
- package/src/lib/components/data-input/configurable-operation-list-input.tsx +58 -0
- package/src/lib/components/data-input/custom-field-list-input.tsx +294 -0
- package/src/lib/components/data-input/customer-group-input.tsx +61 -0
- package/src/lib/components/data-input/datetime-input.stories.tsx +62 -0
- package/src/lib/components/data-input/datetime-input.tsx +192 -0
- package/src/lib/components/data-input/default-relation-input.tsx +626 -0
- package/src/lib/components/data-input/facet-value-input.tsx +78 -0
- package/src/lib/components/data-input/index.ts +23 -0
- package/src/lib/components/data-input/money-input.stories.tsx +88 -0
- package/src/lib/components/data-input/money-input.tsx +122 -0
- package/src/lib/components/data-input/number-input.stories.tsx +103 -0
- package/src/lib/components/data-input/number-input.tsx +85 -0
- package/src/lib/components/data-input/password-form-input.stories.tsx +65 -0
- package/src/lib/components/data-input/password-form-input.tsx +23 -0
- package/src/lib/components/data-input/product-multi-selector-input.tsx +416 -0
- package/src/lib/components/data-input/relation-input.tsx +165 -0
- package/src/lib/components/data-input/relation-selector.tsx +476 -0
- package/src/lib/components/data-input/rich-text-input.stories.tsx +92 -0
- package/src/lib/components/data-input/rich-text-input.tsx +16 -0
- package/src/lib/components/data-input/select-with-options.tsx +96 -0
- package/src/lib/components/data-input/slug-input.stories.tsx +232 -0
- package/src/lib/components/data-input/slug-input.tsx +289 -0
- package/src/lib/components/data-input/string-list-input.tsx +268 -0
- package/src/lib/components/data-input/struct-form-input.tsx +320 -0
- package/src/lib/components/data-input/text-input.stories.tsx +52 -0
- package/src/lib/components/data-input/text-input.tsx +16 -0
- package/src/lib/components/data-input/textarea-input.stories.tsx +55 -0
- package/src/lib/components/data-input/textarea-input.tsx +23 -0
- package/src/lib/components/data-table/add-filter-menu.tsx +64 -0
- package/src/lib/components/data-table/column-header-wrapper.tsx +106 -0
- package/src/lib/components/data-table/data-table-bulk-action-item.tsx +141 -0
- package/src/lib/components/data-table/data-table-bulk-actions.tsx +99 -0
- package/src/lib/components/data-table/data-table-column-header.tsx +50 -0
- package/src/lib/components/data-table/data-table-context.tsx +106 -0
- package/src/lib/components/data-table/data-table-faceted-filter.tsx +195 -0
- package/src/lib/components/data-table/data-table-filter-badge-editable.tsx +35 -0
- package/src/lib/components/data-table/data-table-filter-badge.tsx +101 -0
- package/src/lib/components/data-table/data-table-filter-dialog.tsx +92 -0
- package/src/lib/components/data-table/data-table-pagination.tsx +103 -0
- package/src/lib/components/data-table/data-table-utils.ts +291 -0
- package/src/lib/components/data-table/data-table-view-options.tsx +130 -0
- package/src/lib/components/data-table/data-table.stories.tsx +249 -0
- package/src/lib/components/data-table/data-table.tsx +508 -0
- package/src/lib/components/data-table/filters/data-table-boolean-filter.tsx +60 -0
- package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +159 -0
- package/src/lib/components/data-table/filters/data-table-id-filter.tsx +63 -0
- package/src/lib/components/data-table/filters/data-table-number-filter.tsx +123 -0
- package/src/lib/components/data-table/filters/data-table-string-filter.tsx +79 -0
- package/src/lib/components/data-table/global-views-bar.tsx +97 -0
- package/src/lib/components/data-table/global-views-sheet.tsx +11 -0
- package/src/lib/components/data-table/human-readable-operator.tsx +65 -0
- package/src/lib/components/data-table/manage-global-views-button.tsx +26 -0
- package/src/lib/components/data-table/my-views-button.tsx +47 -0
- package/src/lib/components/data-table/refresh-button.tsx +49 -0
- package/src/lib/components/data-table/save-view-button.tsx +45 -0
- package/src/lib/components/data-table/save-view-dialog.tsx +134 -0
- package/src/lib/components/data-table/types.ts +40 -0
- package/src/lib/components/data-table/use-all-bulk-actions.ts +19 -0
- package/src/lib/components/data-table/use-generated-columns.tsx +411 -0
- package/src/lib/components/data-table/user-views-sheet.tsx +11 -0
- package/src/lib/components/data-table/views-sheet.tsx +305 -0
- package/src/lib/components/date-range-picker.tsx +186 -0
- package/src/lib/components/labeled-data.tsx +21 -0
- package/src/lib/components/layout/app-layout.tsx +35 -0
- package/src/lib/components/layout/app-sidebar.tsx +37 -0
- package/src/lib/components/layout/channel-switcher.tsx +229 -0
- package/src/lib/components/layout/content-language-selector.tsx +46 -0
- package/src/lib/components/layout/dev-mode-indicator.tsx +18 -0
- package/src/lib/components/layout/generated-breadcrumbs.tsx +136 -0
- package/src/lib/components/layout/language-dialog.tsx +133 -0
- package/src/lib/components/layout/manage-languages-dialog.tsx +386 -0
- package/src/lib/components/layout/nav-item-wrapper.tsx +107 -0
- package/src/lib/components/layout/nav-main.tsx +331 -0
- package/src/lib/components/layout/nav-projects.tsx +81 -0
- package/src/lib/components/layout/nav-user.tsx +180 -0
- package/src/lib/components/login/login-form.tsx +124 -0
- package/src/lib/components/shared/alerts.tsx +52 -0
- package/src/lib/components/shared/animated-number.tsx +49 -0
- package/src/lib/components/shared/asset/asset-bulk-actions.tsx +109 -0
- package/src/lib/components/shared/asset/asset-focal-point-editor.tsx +103 -0
- package/src/lib/components/shared/asset/asset-gallery.stories.tsx +76 -0
- package/src/lib/components/shared/asset/asset-gallery.tsx +623 -0
- package/src/lib/components/shared/asset/asset-picker-dialog.stories.tsx +58 -0
- package/src/lib/components/shared/asset/asset-picker-dialog.tsx +109 -0
- package/src/lib/components/shared/asset/asset-preview-dialog.tsx +38 -0
- package/src/lib/components/shared/asset/asset-preview-selector.tsx +32 -0
- package/src/lib/components/shared/asset/asset-preview.tsx +114 -0
- package/src/lib/components/shared/asset/asset-properties.tsx +44 -0
- package/src/lib/components/shared/assign-to-channel-bulk-action.tsx +71 -0
- package/src/lib/components/shared/assign-to-channel-dialog.tsx +156 -0
- package/src/lib/components/shared/assigned-channels.tsx +108 -0
- package/src/lib/components/shared/assigned-facet-values.tsx +61 -0
- package/src/lib/components/shared/channel-chip.tsx +43 -0
- package/src/lib/components/shared/channel-code-label.tsx +6 -0
- package/src/lib/components/shared/channel-selector.tsx +51 -0
- package/src/lib/components/shared/configurable-operation-arg-input.tsx +29 -0
- package/src/lib/components/shared/configurable-operation-input.tsx +167 -0
- package/src/lib/components/shared/configurable-operation-multi-selector.tsx +263 -0
- package/src/lib/components/shared/configurable-operation-selector.tsx +156 -0
- package/src/lib/components/shared/confirmation-dialog.tsx +58 -0
- package/src/lib/components/shared/copyable-text.tsx +30 -0
- package/src/lib/components/shared/country-selector.tsx +108 -0
- package/src/lib/components/shared/currency-selector.tsx +33 -0
- package/src/lib/components/shared/custom-fields-form.tsx +296 -0
- package/src/lib/components/shared/customer-address-form.tsx +338 -0
- package/src/lib/components/shared/customer-group-chip.tsx +30 -0
- package/src/lib/components/shared/customer-group-selector.tsx +65 -0
- package/src/lib/components/shared/customer-selector.tsx +112 -0
- package/src/lib/components/shared/detail-page-button.stories.tsx +52 -0
- package/src/lib/components/shared/detail-page-button.tsx +59 -0
- package/src/lib/components/shared/entity-assets.tsx +411 -0
- package/src/lib/components/shared/error-page.tsx +31 -0
- package/src/lib/components/shared/facet-value-chip.tsx +59 -0
- package/src/lib/components/shared/facet-value-selector.stories.tsx +48 -0
- package/src/lib/components/shared/facet-value-selector.tsx +458 -0
- package/src/lib/components/shared/form-field-wrapper.tsx +111 -0
- package/src/lib/components/shared/history-timeline/history-entry-date.tsx +37 -0
- package/src/lib/components/shared/history-timeline/history-note-checkbox.tsx +28 -0
- package/src/lib/components/shared/history-timeline/history-note-editor.tsx +60 -0
- package/src/lib/components/shared/history-timeline/history-note-entry.tsx +65 -0
- package/src/lib/components/shared/history-timeline/history-note-input.tsx +39 -0
- package/src/lib/components/shared/history-timeline/history-timeline-with-grouping.tsx +141 -0
- package/src/lib/components/shared/history-timeline/history-timeline.tsx +16 -0
- package/src/lib/components/shared/history-timeline/use-history-note-editor.ts +26 -0
- package/src/lib/components/shared/icon-mark.tsx +18 -0
- package/src/lib/components/shared/language-selector.tsx +56 -0
- package/src/lib/components/shared/logo-mark.tsx +24 -0
- package/src/lib/components/shared/multi-select.tsx +159 -0
- package/src/lib/components/shared/navigation-confirmation.tsx +64 -0
- package/src/lib/components/shared/option-value-input.tsx +97 -0
- package/src/lib/components/shared/paginated-list-data-table.stories.tsx +212 -0
- package/src/lib/components/shared/paginated-list-data-table.tsx +523 -0
- package/src/lib/components/shared/permission-guard.stories.tsx +46 -0
- package/src/lib/components/shared/permission-guard.tsx +51 -0
- package/src/lib/components/shared/product-variant-selector.tsx +136 -0
- package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +93 -0
- package/src/lib/components/shared/rich-text-editor/image-dialog.tsx +223 -0
- package/src/lib/components/shared/rich-text-editor/link-dialog.tsx +151 -0
- package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +443 -0
- package/src/lib/components/shared/rich-text-editor/rich-text-editor.tsx +339 -0
- package/src/lib/components/shared/rich-text-editor/table-delete-menu.tsx +104 -0
- package/src/lib/components/shared/rich-text-editor/table-edit-icons.tsx +225 -0
- package/src/lib/components/shared/role-code-label.tsx +12 -0
- package/src/lib/components/shared/role-selector.tsx +56 -0
- package/src/lib/components/shared/seller-selector.tsx +111 -0
- package/src/lib/components/shared/stock-level-label.tsx +22 -0
- package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +80 -0
- package/src/lib/components/shared/tax-category-selector.tsx +65 -0
- package/src/lib/components/shared/translatable-form-field.tsx +149 -0
- package/src/lib/components/shared/vendure-image.stories.tsx +167 -0
- package/src/lib/components/shared/vendure-image.tsx +307 -0
- package/src/lib/components/shared/zone-selector.tsx +65 -0
- package/src/lib/components/ui/accordion.stories.tsx +33 -0
- package/src/lib/components/ui/accordion.tsx +59 -0
- package/src/lib/components/ui/alert-dialog.stories.tsx +48 -0
- package/src/lib/components/ui/alert-dialog.tsx +128 -0
- package/src/lib/components/ui/alert.stories.tsx +35 -0
- package/src/lib/components/ui/alert.tsx +60 -0
- package/src/lib/components/ui/aspect-ratio.stories.tsx +28 -0
- package/src/lib/components/ui/aspect-ratio.tsx +9 -0
- package/src/lib/components/ui/badge.stories.tsx +28 -0
- package/src/lib/components/ui/badge.tsx +38 -0
- package/src/lib/components/ui/breadcrumb.stories.tsx +41 -0
- package/src/lib/components/ui/breadcrumb.tsx +102 -0
- package/src/lib/components/ui/button.stories.tsx +38 -0
- package/src/lib/components/ui/button.tsx +58 -0
- package/src/lib/components/ui/calendar.stories.tsx +22 -0
- package/src/lib/components/ui/calendar.tsx +447 -0
- package/src/lib/components/ui/card.stories.tsx +28 -0
- package/src/lib/components/ui/card.tsx +47 -0
- package/src/lib/components/ui/carousel.stories.tsx +34 -0
- package/src/lib/components/ui/carousel.tsx +241 -0
- package/src/lib/components/ui/chart.tsx +351 -0
- package/src/lib/components/ui/checkbox.stories.tsx +31 -0
- package/src/lib/components/ui/checkbox.tsx +27 -0
- package/src/lib/components/ui/collapsible.stories.tsx +39 -0
- package/src/lib/components/ui/collapsible.tsx +33 -0
- package/src/lib/components/ui/command.stories.tsx +44 -0
- package/src/lib/components/ui/command.tsx +139 -0
- package/src/lib/components/ui/context-menu.stories.tsx +38 -0
- package/src/lib/components/ui/context-menu.tsx +252 -0
- package/src/lib/components/ui/dialog.stories.tsx +52 -0
- package/src/lib/components/ui/dialog.tsx +116 -0
- package/src/lib/components/ui/drawer.stories.tsx +50 -0
- package/src/lib/components/ui/drawer.tsx +133 -0
- package/src/lib/components/ui/dropdown-menu.stories.tsx +41 -0
- package/src/lib/components/ui/dropdown-menu.tsx +223 -0
- package/src/lib/components/ui/form.tsx +141 -0
- package/src/lib/components/ui/grid-layout.tsx +376 -0
- package/src/lib/components/ui/hover-card.stories.tsx +38 -0
- package/src/lib/components/ui/hover-card.tsx +36 -0
- package/src/lib/components/ui/input-group.tsx +149 -0
- package/src/lib/components/ui/input-otp.stories.tsx +30 -0
- package/src/lib/components/ui/input-otp.tsx +77 -0
- package/src/lib/components/ui/input.stories.tsx +38 -0
- package/src/lib/components/ui/input.tsx +19 -0
- package/src/lib/components/ui/label.stories.tsx +24 -0
- package/src/lib/components/ui/label.tsx +21 -0
- package/src/lib/components/ui/menubar.stories.tsx +53 -0
- package/src/lib/components/ui/menubar.tsx +274 -0
- package/src/lib/components/ui/navigation-menu.stories.tsx +54 -0
- package/src/lib/components/ui/navigation-menu.tsx +168 -0
- package/src/lib/components/ui/pagination.stories.tsx +51 -0
- package/src/lib/components/ui/pagination.tsx +106 -0
- package/src/lib/components/ui/password-input.stories.tsx +32 -0
- package/src/lib/components/ui/password-input.tsx +29 -0
- package/src/lib/components/ui/popover.stories.tsx +33 -0
- package/src/lib/components/ui/popover.tsx +40 -0
- package/src/lib/components/ui/progress.stories.tsx +27 -0
- package/src/lib/components/ui/progress.tsx +29 -0
- package/src/lib/components/ui/radio-group.stories.tsx +34 -0
- package/src/lib/components/ui/radio-group.tsx +45 -0
- package/src/lib/components/ui/resizable.stories.tsx +32 -0
- package/src/lib/components/ui/resizable.tsx +54 -0
- package/src/lib/components/ui/scroll-area.stories.tsx +31 -0
- package/src/lib/components/ui/scroll-area.tsx +50 -0
- package/src/lib/components/ui/select.stories.tsx +36 -0
- package/src/lib/components/ui/select.tsx +183 -0
- package/src/lib/components/ui/separator.stories.tsx +35 -0
- package/src/lib/components/ui/separator.tsx +26 -0
- package/src/lib/components/ui/sheet.stories.tsx +50 -0
- package/src/lib/components/ui/sheet.tsx +118 -0
- package/src/lib/components/ui/sidebar-context.ts +16 -0
- package/src/lib/components/ui/sidebar.tsx +686 -0
- package/src/lib/components/ui/skeleton.stories.tsx +26 -0
- package/src/lib/components/ui/skeleton.tsx +13 -0
- package/src/lib/components/ui/slider.stories.tsx +37 -0
- package/src/lib/components/ui/slider.tsx +63 -0
- package/src/lib/components/ui/sonner.tsx +27 -0
- package/src/lib/components/ui/switch.stories.tsx +31 -0
- package/src/lib/components/ui/switch.tsx +26 -0
- package/src/lib/components/ui/table.stories.tsx +52 -0
- package/src/lib/components/ui/table.tsx +82 -0
- package/src/lib/components/ui/tabs.stories.tsx +29 -0
- package/src/lib/components/ui/tabs.tsx +48 -0
- package/src/lib/components/ui/textarea.stories.tsx +32 -0
- package/src/lib/components/ui/textarea.tsx +18 -0
- package/src/lib/components/ui/toggle-group.stories.tsx +31 -0
- package/src/lib/components/ui/toggle-group.tsx +73 -0
- package/src/lib/components/ui/toggle.stories.tsx +39 -0
- package/src/lib/components/ui/toggle.tsx +45 -0
- package/src/lib/components/ui/tooltip.stories.tsx +30 -0
- package/src/lib/components/ui/tooltip.tsx +51 -0
- package/src/lib/constants.ts +336 -0
- package/src/lib/framework/alert/alert-extensions.tsx +19 -0
- package/src/lib/framework/alert/alert-item.tsx +44 -0
- package/src/lib/framework/alert/alerts-indicator.tsx +22 -0
- package/src/lib/framework/alert/search-index-buffer-alert/search-index-buffer-alert.ts +41 -0
- package/src/lib/framework/component-registry/component-registry.tsx +18 -0
- package/src/lib/framework/component-registry/display-component.tsx +28 -0
- package/src/lib/framework/dashboard-widget/base-widget.tsx +101 -0
- package/src/lib/framework/dashboard-widget/latest-orders-widget/index.tsx +152 -0
- package/src/lib/framework/dashboard-widget/latest-orders-widget/latest-orders-widget.graphql.ts +35 -0
- package/src/lib/framework/dashboard-widget/metrics-widget/chart.tsx +40 -0
- package/src/lib/framework/dashboard-widget/metrics-widget/index.tsx +96 -0
- package/src/lib/framework/dashboard-widget/metrics-widget/metrics-widget.graphql.ts +20 -0
- package/src/lib/framework/dashboard-widget/orders-summary/index.tsx +113 -0
- package/src/lib/framework/dashboard-widget/orders-summary/order-summary-widget.graphql.ts +14 -0
- package/src/lib/framework/dashboard-widget/widget-extensions.tsx +19 -0
- package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +36 -0
- package/src/lib/framework/data-table/data-table-extensions.ts +35 -0
- package/src/lib/framework/defaults.ts +270 -0
- package/src/lib/framework/document-extension/extend-detail-form-query.ts +50 -0
- package/src/lib/framework/document-extension/extend-document.spec.ts +884 -0
- package/src/lib/framework/document-extension/extend-document.ts +159 -0
- package/src/lib/framework/document-introspection/add-custom-fields.spec.ts +1458 -0
- package/src/lib/framework/document-introspection/add-custom-fields.ts +452 -0
- package/src/lib/framework/document-introspection/get-document-structure.spec.ts +581 -0
- package/src/lib/framework/document-introspection/get-document-structure.ts +669 -0
- package/src/lib/framework/document-introspection/hooks.ts +13 -0
- package/src/lib/framework/document-introspection/include-only-selected-list-fields.spec.ts +1840 -0
- package/src/lib/framework/document-introspection/include-only-selected-list-fields.ts +940 -0
- package/src/lib/framework/document-introspection/testing-utils.ts +161 -0
- package/src/lib/framework/extension-api/define-dashboard-extension.ts +99 -0
- package/src/lib/framework/extension-api/display-component-extensions.tsx +58 -0
- package/src/lib/framework/extension-api/extension-api-types.ts +92 -0
- package/src/lib/framework/extension-api/input-component-extensions.tsx +96 -0
- package/src/lib/framework/extension-api/logic/alerts.ts +11 -0
- package/src/lib/framework/extension-api/logic/data-table.ts +37 -0
- package/src/lib/framework/extension-api/logic/detail-forms.ts +35 -0
- package/src/lib/framework/extension-api/logic/form-components.ts +14 -0
- package/src/lib/framework/extension-api/logic/history-entries.ts +24 -0
- package/src/lib/framework/extension-api/logic/index.ts +10 -0
- package/src/lib/framework/extension-api/logic/layout.ts +22 -0
- package/src/lib/framework/extension-api/logic/login.ts +17 -0
- package/src/lib/framework/extension-api/logic/navigation.ts +41 -0
- package/src/lib/framework/extension-api/logic/widgets.ts +10 -0
- package/src/lib/framework/extension-api/types/alerts.ts +59 -0
- package/src/lib/framework/extension-api/types/data-table.ts +159 -0
- package/src/lib/framework/extension-api/types/detail-forms.ts +58 -0
- package/src/lib/framework/extension-api/types/form-components.ts +41 -0
- package/src/lib/framework/extension-api/types/history-entries.ts +120 -0
- package/src/lib/framework/extension-api/types/index.ts +10 -0
- package/src/lib/framework/extension-api/types/layout.ts +141 -0
- package/src/lib/framework/extension-api/types/login.ts +76 -0
- package/src/lib/framework/extension-api/types/navigation.ts +105 -0
- package/src/lib/framework/extension-api/types/widgets.ts +101 -0
- package/src/lib/framework/extension-api/use-dashboard-extensions.ts +27 -0
- package/src/lib/framework/extension-api/use-login-extensions.ts +26 -0
- package/src/lib/framework/form-engine/custom-form-component-extensions.ts +15 -0
- package/src/lib/framework/form-engine/custom-form-component.tsx +16 -0
- package/src/lib/framework/form-engine/default-input-for-type.tsx +35 -0
- package/src/lib/framework/form-engine/form-control-adapter.tsx +193 -0
- package/src/lib/framework/form-engine/form-engine-types.ts +169 -0
- package/src/lib/framework/form-engine/form-schema-tools.spec.ts +610 -0
- package/src/lib/framework/form-engine/form-schema-tools.ts +417 -0
- package/src/lib/framework/form-engine/overridden-form-component.tsx +51 -0
- package/src/lib/framework/form-engine/use-generated-form.tsx +226 -0
- package/src/lib/framework/form-engine/utils.spec.ts +37 -0
- package/src/lib/framework/form-engine/utils.ts +356 -0
- package/src/lib/framework/form-engine/value-transformers.ts +150 -0
- package/src/lib/framework/history-entry/history-entry-extensions.ts +11 -0
- package/src/lib/framework/history-entry/history-entry.tsx +129 -0
- package/src/lib/framework/layout-engine/custom-form-page.stories.tsx +344 -0
- package/src/lib/framework/layout-engine/dev-mode-button.tsx +24 -0
- package/src/lib/framework/layout-engine/layout-extensions.ts +27 -0
- package/src/lib/framework/layout-engine/location-wrapper.tsx +122 -0
- package/src/lib/framework/layout-engine/page-block-provider.tsx +6 -0
- package/src/lib/framework/layout-engine/page-layout.spec.tsx +138 -0
- package/src/lib/framework/layout-engine/page-layout.tsx +700 -0
- package/src/lib/framework/layout-engine/page-provider.tsx +10 -0
- package/src/lib/framework/layout-engine/page.stories.tsx +275 -0
- package/src/lib/framework/nav-menu/nav-menu-extensions.ts +112 -0
- package/src/lib/framework/page/detail-page-route-loader.tsx +67 -0
- package/src/lib/framework/page/detail-page.stories.tsx +151 -0
- package/src/lib/framework/page/detail-page.tsx +255 -0
- package/src/lib/framework/page/list-page.stories.tsx +217 -0
- package/src/lib/framework/page/list-page.tsx +613 -0
- package/src/lib/framework/page/page-api.ts +9 -0
- package/src/lib/framework/page/page-types.ts +51 -0
- package/src/lib/framework/page/use-detail-page.ts +341 -0
- package/src/lib/framework/page/use-extended-router.tsx +139 -0
- package/src/lib/framework/registry/global-registry.ts +50 -0
- package/src/lib/framework/registry/registry-types.ts +32 -0
- package/src/lib/graphql/api.ts +123 -0
- package/src/lib/graphql/common-operations.ts +37 -0
- package/src/lib/graphql/fragments.ts +71 -0
- package/src/lib/graphql/graphql-env.d.ts +523 -0
- package/src/lib/graphql/graphql.ts +15 -0
- package/src/lib/graphql/settings-store-operations.ts +17 -0
- package/src/lib/hooks/use-alerts.ts +84 -0
- package/src/lib/hooks/use-auth.tsx +21 -0
- package/src/lib/hooks/use-channel.ts +27 -0
- package/src/lib/hooks/use-custom-field-config.ts +27 -0
- package/src/lib/hooks/use-display-locale.ts +40 -0
- package/src/lib/hooks/use-drag-and-drop.ts +86 -0
- package/src/lib/hooks/use-dynamic-translations.ts +46 -0
- package/src/lib/hooks/use-extended-detail-query.ts +37 -0
- package/src/lib/hooks/use-extended-list-query.ts +80 -0
- package/src/lib/hooks/use-floating-bulk-actions.ts +83 -0
- package/src/lib/hooks/use-grouped-permissions.ts +55 -0
- package/src/lib/hooks/use-local-format.ts +186 -0
- package/src/lib/hooks/use-mobile.ts +19 -0
- package/src/lib/hooks/use-page-block.tsx +27 -0
- package/src/lib/hooks/use-page.tsx +10 -0
- package/src/lib/hooks/use-permissions.ts +40 -0
- package/src/lib/hooks/use-saved-views.ts +238 -0
- package/src/lib/hooks/use-server-config.ts +4 -0
- package/src/lib/hooks/use-sorted-languages.ts +41 -0
- package/src/lib/hooks/use-theme.ts +10 -0
- package/src/lib/hooks/use-ui-language-loader.ts +30 -0
- package/src/lib/hooks/use-user-settings.tsx +12 -0
- package/src/lib/index.ts +293 -0
- package/src/lib/lib/load-i18n-messages.ts +20 -0
- package/src/lib/lib/trans.tsx +20 -0
- package/src/lib/lib/utils.ts +111 -0
- package/src/lib/providers/alerts-provider.tsx +60 -0
- package/src/lib/providers/auth.tsx +232 -0
- package/src/lib/providers/channel-provider.tsx +245 -0
- package/src/lib/providers/i18n-provider.tsx +21 -0
- package/src/lib/providers/server-config.tsx +289 -0
- package/src/lib/providers/theme-provider.tsx +56 -0
- package/src/lib/providers/user-settings.tsx +244 -0
- package/src/lib/types/saved-views.ts +42 -0
- package/src/lib/utils/config-utils.ts +19 -0
- package/src/lib/utils/global-languages.ts +268 -0
- package/src/lib/utils/saved-views-utils.ts +40 -0
- package/src/lib/virtual.d.ts +39 -0
- package/src/vite-env.d.ts +2 -0
|
@@ -0,0 +1,1458 @@
|
|
|
1
|
+
import { CustomFieldConfig, CustomFields } from '@vendure/common/lib/generated-types';
|
|
2
|
+
import { graphql } from 'gql.tada';
|
|
3
|
+
import { DocumentNode, FieldNode, FragmentDefinitionNode, Kind, print } from 'graphql';
|
|
4
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import { addCustomFields, addCustomFieldsToFragment } from './add-custom-fields.js';
|
|
7
|
+
|
|
8
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
9
|
+
describe('addCustomFields()', () => {
|
|
10
|
+
/**
|
|
11
|
+
* Normalizes the indentation of a string to make it easier to compare with the expected output
|
|
12
|
+
*/
|
|
13
|
+
function normalizeIndentation(str: string): string {
|
|
14
|
+
const lines = str.replace(/ /g, ' ').split('\n');
|
|
15
|
+
const indentLength = lines[1].search(/\S|$/); // Find the first non-whitespace character
|
|
16
|
+
return lines
|
|
17
|
+
.map(line => line.slice(indentLength))
|
|
18
|
+
.join('\n')
|
|
19
|
+
.trim()
|
|
20
|
+
.replace(/"/g, '');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('Query handling', () => {
|
|
24
|
+
it('Adds customFields to entity query', () => {
|
|
25
|
+
const documentNode = graphql(`
|
|
26
|
+
query GetProduct {
|
|
27
|
+
product {
|
|
28
|
+
id
|
|
29
|
+
name
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`);
|
|
33
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
34
|
+
customFieldsConfig.set('Product', [
|
|
35
|
+
{ name: 'custom1', type: 'string', list: false },
|
|
36
|
+
{ name: 'custom2', type: 'boolean', list: false },
|
|
37
|
+
]);
|
|
38
|
+
const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
|
|
39
|
+
|
|
40
|
+
expect(print(result)).toBe(
|
|
41
|
+
normalizeIndentation(`
|
|
42
|
+
query GetProduct {
|
|
43
|
+
product {
|
|
44
|
+
id
|
|
45
|
+
name
|
|
46
|
+
customFields {
|
|
47
|
+
custom1
|
|
48
|
+
custom2
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
`),
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('Adds customFields to paginated list', () => {
|
|
57
|
+
const documentNode = graphql(`
|
|
58
|
+
query GetProducts {
|
|
59
|
+
products {
|
|
60
|
+
items {
|
|
61
|
+
id
|
|
62
|
+
name
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
`);
|
|
67
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
68
|
+
customFieldsConfig.set('Product', [
|
|
69
|
+
{ name: 'custom1', type: 'string', list: false },
|
|
70
|
+
{ name: 'custom2', type: 'boolean', list: false },
|
|
71
|
+
]);
|
|
72
|
+
const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
|
|
73
|
+
expect(print(result)).toBe(
|
|
74
|
+
normalizeIndentation(`
|
|
75
|
+
query GetProducts {
|
|
76
|
+
products {
|
|
77
|
+
items {
|
|
78
|
+
id
|
|
79
|
+
name
|
|
80
|
+
customFields {
|
|
81
|
+
custom1
|
|
82
|
+
custom2
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
`),
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('Fragment handling', () => {
|
|
93
|
+
const productWithVariantsFragment = graphql(`
|
|
94
|
+
fragment ProductWithVariants on Product {
|
|
95
|
+
id
|
|
96
|
+
translations {
|
|
97
|
+
languageCode
|
|
98
|
+
name
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
`);
|
|
102
|
+
|
|
103
|
+
const productVariantFragment = graphql(`
|
|
104
|
+
fragment ProductVariant on ProductVariant {
|
|
105
|
+
id
|
|
106
|
+
}
|
|
107
|
+
`);
|
|
108
|
+
|
|
109
|
+
const productOptionGroupFragment = graphql(`
|
|
110
|
+
fragment ProductOptionGroup on ProductOptionGroup {
|
|
111
|
+
id
|
|
112
|
+
}
|
|
113
|
+
`);
|
|
114
|
+
|
|
115
|
+
const productOptionFragment = graphql(`
|
|
116
|
+
fragment ProductOption on ProductOption {
|
|
117
|
+
id
|
|
118
|
+
}
|
|
119
|
+
`);
|
|
120
|
+
|
|
121
|
+
const userFragment = graphql(`
|
|
122
|
+
fragment User on User {
|
|
123
|
+
id
|
|
124
|
+
}
|
|
125
|
+
`);
|
|
126
|
+
|
|
127
|
+
const customerFragment = graphql(`
|
|
128
|
+
fragment Customer on Customer {
|
|
129
|
+
id
|
|
130
|
+
}
|
|
131
|
+
`);
|
|
132
|
+
|
|
133
|
+
const addressFragment = graphql(`
|
|
134
|
+
fragment Address on Address {
|
|
135
|
+
id
|
|
136
|
+
}
|
|
137
|
+
`);
|
|
138
|
+
|
|
139
|
+
let documentNode: DocumentNode;
|
|
140
|
+
|
|
141
|
+
beforeEach(() => {
|
|
142
|
+
documentNode = graphql(
|
|
143
|
+
`
|
|
144
|
+
query GetProductWithVariants {
|
|
145
|
+
product {
|
|
146
|
+
...ProductWithVariants
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
`,
|
|
150
|
+
[productWithVariantsFragment],
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('Adds customFields to Product fragment', () => {
|
|
155
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
156
|
+
customFieldsConfig.set('Product', [
|
|
157
|
+
{ name: 'custom1', type: 'string', list: false },
|
|
158
|
+
{ name: 'custom2', type: 'boolean', list: false },
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
|
|
162
|
+
const productFragmentDef = result.definitions[1] as FragmentDefinitionNode;
|
|
163
|
+
const customFieldsDef = productFragmentDef.selectionSet.selections[2] as FieldNode;
|
|
164
|
+
expect(productFragmentDef.selectionSet.selections.length).toBe(3);
|
|
165
|
+
expect(customFieldsDef.selectionSet!.selections.length).toBe(2);
|
|
166
|
+
expect((customFieldsDef.selectionSet!.selections[0] as FieldNode).name.value).toBe('custom1');
|
|
167
|
+
expect((customFieldsDef.selectionSet!.selections[1] as FieldNode).name.value).toBe('custom2');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('Adds customFields to Product translations', () => {
|
|
171
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
172
|
+
customFieldsConfig.set('Product', [
|
|
173
|
+
{ name: 'customLocaleString', type: 'localeString', list: false },
|
|
174
|
+
]);
|
|
175
|
+
|
|
176
|
+
const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
|
|
177
|
+
const productFragmentDef = result.definitions[1] as FragmentDefinitionNode;
|
|
178
|
+
const translationsField = productFragmentDef.selectionSet.selections[1] as FieldNode;
|
|
179
|
+
const customTranslationFieldsDef = translationsField.selectionSet!.selections[2] as FieldNode;
|
|
180
|
+
expect(translationsField.selectionSet!.selections.length).toBe(3);
|
|
181
|
+
expect((customTranslationFieldsDef.selectionSet!.selections[0] as FieldNode).name.value).toBe(
|
|
182
|
+
'customLocaleString',
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
function addsCustomFieldsToType(type: keyof CustomFields, fragment: DocumentNode) {
|
|
187
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
188
|
+
customFieldsConfig.set(type, [{ name: 'custom', type: 'boolean', list: false }]);
|
|
189
|
+
|
|
190
|
+
const result = addCustomFields(fragment, { customFieldsMap: customFieldsConfig });
|
|
191
|
+
const fragmentDef = result.definitions[0] as FragmentDefinitionNode;
|
|
192
|
+
const customFieldsDef = fragmentDef.selectionSet.selections[1] as FieldNode;
|
|
193
|
+
expect(fragmentDef.selectionSet.selections.length).toBe(2);
|
|
194
|
+
expect(customFieldsDef.selectionSet!.selections.length).toBe(1);
|
|
195
|
+
expect((customFieldsDef.selectionSet!.selections[0] as FieldNode).name.value).toBe('custom');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
it('Does not duplicate customFields selection set', () => {
|
|
199
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
200
|
+
customFieldsConfig.set('Product', [{ name: 'custom', type: 'boolean', list: false }]);
|
|
201
|
+
const result1 = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
|
|
202
|
+
const result2 = addCustomFields(result1, { customFieldsMap: customFieldsConfig });
|
|
203
|
+
|
|
204
|
+
const fragmentDef = result2.definitions[1] as FragmentDefinitionNode;
|
|
205
|
+
const customFieldSelections = fragmentDef.selectionSet.selections.filter(
|
|
206
|
+
s => s.kind === Kind.FIELD && s.name.value === 'customFields',
|
|
207
|
+
);
|
|
208
|
+
expect(customFieldSelections.length).toBe(1);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('Adds customFields to ProductVariant fragment', () => {
|
|
212
|
+
addsCustomFieldsToType('ProductVariant', productVariantFragment);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('Adds customFields to ProductOptionGroup fragment', () => {
|
|
216
|
+
addsCustomFieldsToType('ProductOptionGroup', productOptionGroupFragment);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('Adds customFields to ProductOption fragment', () => {
|
|
220
|
+
addsCustomFieldsToType('ProductOption', productOptionFragment);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('Adds customFields to User fragment', () => {
|
|
224
|
+
addsCustomFieldsToType('User', userFragment);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('Adds customFields to Customer fragment', () => {
|
|
228
|
+
addsCustomFieldsToType('Customer', customerFragment);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('Adds customFields to Address fragment', () => {
|
|
232
|
+
addsCustomFieldsToType('Address', addressFragment);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe('Nested entity handling', () => {
|
|
237
|
+
it('User example: Should not add custom fields to Asset fragment used in nested featuredAsset', () => {
|
|
238
|
+
const assetFragment = graphql(`
|
|
239
|
+
fragment Asset on Asset {
|
|
240
|
+
id
|
|
241
|
+
createdAt
|
|
242
|
+
updatedAt
|
|
243
|
+
name
|
|
244
|
+
fileSize
|
|
245
|
+
mimeType
|
|
246
|
+
type
|
|
247
|
+
preview
|
|
248
|
+
source
|
|
249
|
+
width
|
|
250
|
+
height
|
|
251
|
+
focalPoint {
|
|
252
|
+
x
|
|
253
|
+
y
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
`);
|
|
257
|
+
|
|
258
|
+
const documentNode = graphql(
|
|
259
|
+
`
|
|
260
|
+
query CollectionList($options: CollectionListOptions) {
|
|
261
|
+
collections(options: $options) {
|
|
262
|
+
items {
|
|
263
|
+
id
|
|
264
|
+
createdAt
|
|
265
|
+
updatedAt
|
|
266
|
+
featuredAsset {
|
|
267
|
+
...Asset
|
|
268
|
+
}
|
|
269
|
+
name
|
|
270
|
+
slug
|
|
271
|
+
breadcrumbs {
|
|
272
|
+
id
|
|
273
|
+
name
|
|
274
|
+
slug
|
|
275
|
+
}
|
|
276
|
+
children {
|
|
277
|
+
id
|
|
278
|
+
name
|
|
279
|
+
}
|
|
280
|
+
position
|
|
281
|
+
isPrivate
|
|
282
|
+
parentId
|
|
283
|
+
productVariants {
|
|
284
|
+
totalItems
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
totalItems
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
`,
|
|
291
|
+
[assetFragment],
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
295
|
+
customFieldsConfig.set('Collection', [
|
|
296
|
+
{ name: 'featuredProducts', type: 'relation', list: true, scalarFields: ['id', 'name'] },
|
|
297
|
+
{ name: 'alternativeAsset', type: 'relation', list: false, scalarFields: ['id', 'name'] },
|
|
298
|
+
]);
|
|
299
|
+
customFieldsConfig.set('Asset', [
|
|
300
|
+
{ name: 'assetCustomField1', type: 'string', list: false },
|
|
301
|
+
{ name: 'assetCustomField2', type: 'string', list: false },
|
|
302
|
+
{ name: 'assetCustomField3', type: 'string', list: false },
|
|
303
|
+
]);
|
|
304
|
+
|
|
305
|
+
const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
|
|
306
|
+
const printed = print(result);
|
|
307
|
+
|
|
308
|
+
// Should add customFields ONLY to Collection items (top-level entity)
|
|
309
|
+
expect(printed).toContain('customFields {');
|
|
310
|
+
expect(printed).toContain('featuredProducts {');
|
|
311
|
+
expect(printed).toContain('alternativeAsset {');
|
|
312
|
+
|
|
313
|
+
// Should NOT add customFields to Asset fragment (only used in nested context)
|
|
314
|
+
const fragmentMatch = printed.match(/fragment Asset on Asset\s*\{[^}]*\}/s);
|
|
315
|
+
expect(fragmentMatch).toBeTruthy();
|
|
316
|
+
expect(fragmentMatch![0]).not.toContain('customFields');
|
|
317
|
+
expect(fragmentMatch![0]).not.toContain('assetCustomField1');
|
|
318
|
+
|
|
319
|
+
// Should NOT add customFields to children (nested Collection entities)
|
|
320
|
+
const childrenMatch = printed.match(/children\s*\{[^}]+\}/s);
|
|
321
|
+
expect(childrenMatch).toBeTruthy();
|
|
322
|
+
expect(childrenMatch![0]).not.toContain('customFields');
|
|
323
|
+
|
|
324
|
+
// Should NOT add customFields to breadcrumbs
|
|
325
|
+
const breadcrumbsMatch = printed.match(/breadcrumbs\s*\{[^}]+\}/s);
|
|
326
|
+
expect(breadcrumbsMatch).toBeTruthy();
|
|
327
|
+
expect(breadcrumbsMatch![0]).not.toContain('customFields');
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('Should only add custom fields to top-level entity, not nested related entities', () => {
|
|
331
|
+
const documentNode = graphql(`
|
|
332
|
+
query CollectionList($options: CollectionListOptions) {
|
|
333
|
+
collections(options: $options) {
|
|
334
|
+
items {
|
|
335
|
+
id
|
|
336
|
+
name
|
|
337
|
+
slug
|
|
338
|
+
featuredAsset {
|
|
339
|
+
id
|
|
340
|
+
preview
|
|
341
|
+
}
|
|
342
|
+
children {
|
|
343
|
+
id
|
|
344
|
+
name
|
|
345
|
+
slug
|
|
346
|
+
}
|
|
347
|
+
breadcrumbs {
|
|
348
|
+
id
|
|
349
|
+
name
|
|
350
|
+
slug
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
totalItems
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
`);
|
|
357
|
+
|
|
358
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
359
|
+
customFieldsConfig.set('Collection', [
|
|
360
|
+
{ name: 'featuredProducts', type: 'relation', list: true, scalarFields: ['id', 'name'] },
|
|
361
|
+
{ name: 'alternativeAsset', type: 'relation', list: false, scalarFields: ['id', 'name'] },
|
|
362
|
+
]);
|
|
363
|
+
customFieldsConfig.set('Asset', [
|
|
364
|
+
{ name: 'assetCustomField1', type: 'string', list: false },
|
|
365
|
+
{ name: 'assetCustomField2', type: 'string', list: false },
|
|
366
|
+
]);
|
|
367
|
+
|
|
368
|
+
const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
|
|
369
|
+
const printed = print(result);
|
|
370
|
+
|
|
371
|
+
// Should add customFields to top-level Collection (items)
|
|
372
|
+
expect(printed).toContain('customFields {');
|
|
373
|
+
expect(printed).toContain('featuredProducts {');
|
|
374
|
+
expect(printed).toContain('alternativeAsset {');
|
|
375
|
+
|
|
376
|
+
// Should NOT add customFields to nested Collection entities (children, breadcrumbs)
|
|
377
|
+
const childrenMatch = printed.match(/children\s*\{[^}]+\}/s);
|
|
378
|
+
const breadcrumbsMatch = printed.match(/breadcrumbs\s*\{[^}]+\}/s);
|
|
379
|
+
|
|
380
|
+
expect(childrenMatch).toBeTruthy();
|
|
381
|
+
expect(breadcrumbsMatch).toBeTruthy();
|
|
382
|
+
expect(childrenMatch![0]).not.toContain('customFields');
|
|
383
|
+
expect(breadcrumbsMatch![0]).not.toContain('customFields');
|
|
384
|
+
|
|
385
|
+
// Should NOT add customFields to nested Asset entity (featuredAsset)
|
|
386
|
+
const featuredAssetMatch = printed.match(/featuredAsset\s*\{[^}]+\}/s);
|
|
387
|
+
expect(featuredAssetMatch).toBeTruthy();
|
|
388
|
+
expect(featuredAssetMatch![0]).not.toContain('customFields');
|
|
389
|
+
expect(featuredAssetMatch![0]).not.toContain('assetCustomField1');
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('Should NOT add custom fields to fragments that are only used in nested contexts', () => {
|
|
393
|
+
const assetFragment = graphql(`
|
|
394
|
+
fragment Asset on Asset {
|
|
395
|
+
id
|
|
396
|
+
name
|
|
397
|
+
preview
|
|
398
|
+
}
|
|
399
|
+
`);
|
|
400
|
+
|
|
401
|
+
const documentNode = graphql(
|
|
402
|
+
`
|
|
403
|
+
query ProductList($options: ProductListOptions) {
|
|
404
|
+
products(options: $options) {
|
|
405
|
+
items {
|
|
406
|
+
id
|
|
407
|
+
name
|
|
408
|
+
featuredAsset {
|
|
409
|
+
...Asset
|
|
410
|
+
}
|
|
411
|
+
variants {
|
|
412
|
+
id
|
|
413
|
+
name
|
|
414
|
+
assets {
|
|
415
|
+
...Asset
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
totalItems
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
`,
|
|
423
|
+
[assetFragment],
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
427
|
+
customFieldsConfig.set('Product', [{ name: 'productCustomField', type: 'string', list: false }]);
|
|
428
|
+
customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
|
|
429
|
+
customFieldsConfig.set('ProductVariant', [
|
|
430
|
+
{ name: 'variantCustomField', type: 'string', list: false },
|
|
431
|
+
]);
|
|
432
|
+
|
|
433
|
+
const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
|
|
434
|
+
const printed = print(result);
|
|
435
|
+
|
|
436
|
+
// Should add customFields to Product (top-level query entity)
|
|
437
|
+
expect(printed).toContain('customFields {');
|
|
438
|
+
expect(printed).toContain('productCustomField');
|
|
439
|
+
|
|
440
|
+
// Should NOT add customFields to Asset fragment (only used in nested contexts)
|
|
441
|
+
expect(printed).not.toMatch(/fragment Asset on Asset\s*\{[^}]*customFields/s);
|
|
442
|
+
|
|
443
|
+
// Should NOT add customFields to nested ProductVariant entities
|
|
444
|
+
const variantsMatch = printed.match(/variants\s*\{[^}]+\}/s);
|
|
445
|
+
expect(variantsMatch).toBeTruthy();
|
|
446
|
+
expect(variantsMatch![0]).not.toContain('customFields');
|
|
447
|
+
expect(variantsMatch![0]).not.toContain('variantCustomField');
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('Should add custom fields to fragments used at top level', () => {
|
|
451
|
+
const productFragment = graphql(`
|
|
452
|
+
fragment ProductDetails on Product {
|
|
453
|
+
id
|
|
454
|
+
name
|
|
455
|
+
slug
|
|
456
|
+
}
|
|
457
|
+
`);
|
|
458
|
+
|
|
459
|
+
const documentNode = graphql(
|
|
460
|
+
`
|
|
461
|
+
query ProductList($options: ProductListOptions) {
|
|
462
|
+
products(options: $options) {
|
|
463
|
+
items {
|
|
464
|
+
...ProductDetails
|
|
465
|
+
featuredAsset {
|
|
466
|
+
id
|
|
467
|
+
preview
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
totalItems
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
`,
|
|
474
|
+
[productFragment],
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
478
|
+
customFieldsConfig.set('Product', [{ name: 'productCustomField', type: 'string', list: false }]);
|
|
479
|
+
customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
|
|
480
|
+
|
|
481
|
+
const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
|
|
482
|
+
const printed = print(result);
|
|
483
|
+
|
|
484
|
+
// Should add customFields to ProductDetails fragment (used at top level in items)
|
|
485
|
+
expect(printed).toContain('fragment ProductDetails on Product');
|
|
486
|
+
expect(printed).toContain('productCustomField');
|
|
487
|
+
|
|
488
|
+
// Should NOT add customFields to featuredAsset (nested entity)
|
|
489
|
+
const featuredAssetMatch = printed.match(/featuredAsset\s*\{[^}]+\}/s);
|
|
490
|
+
expect(featuredAssetMatch).toBeTruthy();
|
|
491
|
+
expect(featuredAssetMatch![0]).not.toContain('customFields');
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it('Should handle complex nested structure with multiple entity types', () => {
|
|
495
|
+
const documentNode = graphql(`
|
|
496
|
+
query ComplexQuery {
|
|
497
|
+
orders {
|
|
498
|
+
items {
|
|
499
|
+
id
|
|
500
|
+
code
|
|
501
|
+
customer {
|
|
502
|
+
id
|
|
503
|
+
firstName
|
|
504
|
+
addresses {
|
|
505
|
+
id
|
|
506
|
+
streetLine1
|
|
507
|
+
country {
|
|
508
|
+
id
|
|
509
|
+
name
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
lines {
|
|
514
|
+
id
|
|
515
|
+
productVariant {
|
|
516
|
+
id
|
|
517
|
+
name
|
|
518
|
+
product {
|
|
519
|
+
id
|
|
520
|
+
name
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
`);
|
|
528
|
+
|
|
529
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
530
|
+
customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
|
|
531
|
+
customFieldsConfig.set('Customer', [
|
|
532
|
+
{ name: 'customerCustomField', type: 'string', list: false },
|
|
533
|
+
]);
|
|
534
|
+
customFieldsConfig.set('Address', [{ name: 'addressCustomField', type: 'string', list: false }]);
|
|
535
|
+
customFieldsConfig.set('Product', [{ name: 'productCustomField', type: 'string', list: false }]);
|
|
536
|
+
|
|
537
|
+
const result = addCustomFields(documentNode, { customFieldsMap: customFieldsConfig });
|
|
538
|
+
const printed = print(result);
|
|
539
|
+
|
|
540
|
+
// Should only add customFields to top-level Order entity
|
|
541
|
+
expect(printed).toContain('customFields {');
|
|
542
|
+
expect(printed).toContain('orderCustomField');
|
|
543
|
+
|
|
544
|
+
// Should NOT add customFields to any nested entities
|
|
545
|
+
expect(printed).not.toMatch(/customer\s*\{[^}]*customFields/s);
|
|
546
|
+
expect(printed).not.toMatch(/addresses\s*\{[^}]*customFields/s);
|
|
547
|
+
expect(printed).not.toMatch(/country\s*\{[^}]*customFields/s);
|
|
548
|
+
expect(printed).not.toMatch(/productVariant\s*\{[^}]*customFields/s);
|
|
549
|
+
expect(printed).not.toMatch(/product\s*\{[^}]*customFields/s);
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
describe('includeNestedFragments option', () => {
|
|
554
|
+
it('Should add custom fields to nested fragments when explicitly included', () => {
|
|
555
|
+
const assetFragment = graphql(`
|
|
556
|
+
fragment Asset on Asset {
|
|
557
|
+
id
|
|
558
|
+
preview
|
|
559
|
+
}
|
|
560
|
+
`);
|
|
561
|
+
|
|
562
|
+
const documentNode = graphql(
|
|
563
|
+
`
|
|
564
|
+
query ProductList($options: ProductListOptions) {
|
|
565
|
+
products(options: $options) {
|
|
566
|
+
items {
|
|
567
|
+
id
|
|
568
|
+
name
|
|
569
|
+
featuredAsset {
|
|
570
|
+
...Asset
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
`,
|
|
576
|
+
[assetFragment],
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
580
|
+
customFieldsConfig.set('Product', [{ name: 'productCustomField', type: 'string', list: false }]);
|
|
581
|
+
customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
|
|
582
|
+
|
|
583
|
+
const result = addCustomFields(documentNode, {
|
|
584
|
+
customFieldsMap: customFieldsConfig,
|
|
585
|
+
includeNestedFragments: ['Asset'], // Explicitly include the nested Asset fragment
|
|
586
|
+
});
|
|
587
|
+
const printed = print(result);
|
|
588
|
+
|
|
589
|
+
// Should add customFields to Product (top-level)
|
|
590
|
+
expect(printed).toContain('productCustomField');
|
|
591
|
+
|
|
592
|
+
// Should ALSO add customFields to Asset (nested, but explicitly included)
|
|
593
|
+
expect(printed).toContain('fragment Asset on Asset');
|
|
594
|
+
expect(printed).toContain('assetCustomField');
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it('Should handle multiple nested fragments in includeNestedFragments', () => {
|
|
598
|
+
const assetFragment = graphql(`
|
|
599
|
+
fragment Asset on Asset {
|
|
600
|
+
id
|
|
601
|
+
preview
|
|
602
|
+
}
|
|
603
|
+
`);
|
|
604
|
+
|
|
605
|
+
const orderLineFragment = graphql(`
|
|
606
|
+
fragment OrderLine on OrderLine {
|
|
607
|
+
id
|
|
608
|
+
quantity
|
|
609
|
+
}
|
|
610
|
+
`);
|
|
611
|
+
|
|
612
|
+
const documentNode = graphql(
|
|
613
|
+
`
|
|
614
|
+
query GetOrder($id: ID!) {
|
|
615
|
+
order(id: $id) {
|
|
616
|
+
id
|
|
617
|
+
code
|
|
618
|
+
lines {
|
|
619
|
+
...OrderLine
|
|
620
|
+
}
|
|
621
|
+
featuredAsset {
|
|
622
|
+
...Asset
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
`,
|
|
627
|
+
[orderLineFragment, assetFragment],
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
631
|
+
customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
|
|
632
|
+
customFieldsConfig.set('OrderLine', [
|
|
633
|
+
{ name: 'orderLineCustomField', type: 'string', list: false },
|
|
634
|
+
]);
|
|
635
|
+
customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
|
|
636
|
+
|
|
637
|
+
const result = addCustomFields(documentNode, {
|
|
638
|
+
customFieldsMap: customFieldsConfig,
|
|
639
|
+
includeNestedFragments: ['OrderLine', 'Asset'], // Include both nested fragments
|
|
640
|
+
});
|
|
641
|
+
const printed = print(result);
|
|
642
|
+
|
|
643
|
+
// Should add customFields to all three
|
|
644
|
+
expect(printed).toContain('orderCustomField');
|
|
645
|
+
expect(printed).toContain('orderLineCustomField');
|
|
646
|
+
expect(printed).toContain('assetCustomField');
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('Should only add custom fields to specified nested fragments, not all nested fragments', () => {
|
|
650
|
+
const assetFragment = graphql(`
|
|
651
|
+
fragment Asset on Asset {
|
|
652
|
+
id
|
|
653
|
+
preview
|
|
654
|
+
}
|
|
655
|
+
`);
|
|
656
|
+
|
|
657
|
+
const orderLineFragment = graphql(`
|
|
658
|
+
fragment OrderLine on OrderLine {
|
|
659
|
+
id
|
|
660
|
+
quantity
|
|
661
|
+
}
|
|
662
|
+
`);
|
|
663
|
+
|
|
664
|
+
const documentNode = graphql(
|
|
665
|
+
`
|
|
666
|
+
query GetOrder($id: ID!) {
|
|
667
|
+
order(id: $id) {
|
|
668
|
+
id
|
|
669
|
+
lines {
|
|
670
|
+
...OrderLine
|
|
671
|
+
}
|
|
672
|
+
featuredAsset {
|
|
673
|
+
...Asset
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
`,
|
|
678
|
+
[orderLineFragment, assetFragment],
|
|
679
|
+
);
|
|
680
|
+
|
|
681
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
682
|
+
customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
|
|
683
|
+
customFieldsConfig.set('OrderLine', [
|
|
684
|
+
{ name: 'orderLineCustomField', type: 'string', list: false },
|
|
685
|
+
]);
|
|
686
|
+
customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
|
|
687
|
+
|
|
688
|
+
const result = addCustomFields(documentNode, {
|
|
689
|
+
customFieldsMap: customFieldsConfig,
|
|
690
|
+
includeNestedFragments: ['OrderLine'], // Only include OrderLine, not Asset
|
|
691
|
+
});
|
|
692
|
+
const printed = print(result);
|
|
693
|
+
|
|
694
|
+
// Should add customFields to Order (top-level) and OrderLine (explicitly included)
|
|
695
|
+
expect(printed).toContain('orderCustomField');
|
|
696
|
+
expect(printed).toContain('orderLineCustomField');
|
|
697
|
+
|
|
698
|
+
// Should NOT add customFields to Asset (nested and not included)
|
|
699
|
+
expect(printed).not.toContain('assetCustomField');
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
it('Works with the timing issue - called later when globalCustomFieldsMap is populated', () => {
|
|
703
|
+
const orderLineFragment = graphql(`
|
|
704
|
+
fragment OrderLine on OrderLine {
|
|
705
|
+
id
|
|
706
|
+
quantity
|
|
707
|
+
}
|
|
708
|
+
`);
|
|
709
|
+
|
|
710
|
+
const orderDetailFragment = graphql(
|
|
711
|
+
`
|
|
712
|
+
fragment OrderDetail on Order {
|
|
713
|
+
id
|
|
714
|
+
code
|
|
715
|
+
lines {
|
|
716
|
+
...OrderLine
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
`,
|
|
720
|
+
[orderLineFragment],
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
const orderDetailDocument = graphql(
|
|
724
|
+
`
|
|
725
|
+
query GetOrder($id: ID!) {
|
|
726
|
+
order(id: $id) {
|
|
727
|
+
...OrderDetail
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
`,
|
|
731
|
+
[orderDetailFragment],
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
// Initially, globalCustomFieldsMap is empty (simulating module load time)
|
|
735
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
736
|
+
// Documents are created...
|
|
737
|
+
|
|
738
|
+
// Later, when server config is loaded and custom fields are available
|
|
739
|
+
customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
|
|
740
|
+
customFieldsConfig.set('OrderLine', [
|
|
741
|
+
{ name: 'orderLineCustomField', type: 'string', list: false },
|
|
742
|
+
]);
|
|
743
|
+
|
|
744
|
+
// Now when addCustomFields is called (e.g., in a component), it has access to custom fields
|
|
745
|
+
const result = addCustomFields(orderDetailDocument, {
|
|
746
|
+
customFieldsMap: customFieldsConfig,
|
|
747
|
+
includeNestedFragments: ['OrderLine'], // Explicitly include nested OrderLine fragment
|
|
748
|
+
});
|
|
749
|
+
const printed = print(result);
|
|
750
|
+
|
|
751
|
+
// Should add customFields to both Order and OrderLine
|
|
752
|
+
expect(printed).toContain('orderCustomField');
|
|
753
|
+
expect(printed).toContain('orderLineCustomField');
|
|
754
|
+
});
|
|
755
|
+
});
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
describe('addCustomFieldsToFragment()', () => {
|
|
759
|
+
/**
|
|
760
|
+
* Normalizes the indentation of a string to make it easier to compare with the expected output
|
|
761
|
+
*/
|
|
762
|
+
function normalizeIndentation(str: string): string {
|
|
763
|
+
const lines = str.replace(/ /g, ' ').split('\n');
|
|
764
|
+
const indentLength = lines[1].search(/\S|$/); // Find the first non-whitespace character
|
|
765
|
+
return lines
|
|
766
|
+
.map(line => line.slice(indentLength))
|
|
767
|
+
.join('\n')
|
|
768
|
+
.trim()
|
|
769
|
+
.replace(/"/g, '');
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
describe('Basic functionality', () => {
|
|
773
|
+
it('Adds customFields to a simple fragment', () => {
|
|
774
|
+
const fragmentDocument = graphql(`
|
|
775
|
+
fragment Product on Product {
|
|
776
|
+
id
|
|
777
|
+
name
|
|
778
|
+
}
|
|
779
|
+
`);
|
|
780
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
781
|
+
customFieldsConfig.set('Product', [
|
|
782
|
+
{ name: 'custom1', type: 'string', list: false },
|
|
783
|
+
{ name: 'custom2', type: 'boolean', list: false },
|
|
784
|
+
]);
|
|
785
|
+
|
|
786
|
+
const result = addCustomFieldsToFragment(fragmentDocument, {
|
|
787
|
+
customFieldsMap: customFieldsConfig,
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
expect(print(result)).toBe(
|
|
791
|
+
normalizeIndentation(`
|
|
792
|
+
fragment Product on Product {
|
|
793
|
+
id
|
|
794
|
+
name
|
|
795
|
+
customFields {
|
|
796
|
+
custom1
|
|
797
|
+
custom2
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
`),
|
|
801
|
+
);
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
it('Adds customFields with includeCustomFields filter', () => {
|
|
805
|
+
const fragmentDocument = graphql(`
|
|
806
|
+
fragment Product on Product {
|
|
807
|
+
id
|
|
808
|
+
name
|
|
809
|
+
}
|
|
810
|
+
`);
|
|
811
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
812
|
+
customFieldsConfig.set('Product', [
|
|
813
|
+
{ name: 'custom1', type: 'string', list: false },
|
|
814
|
+
{ name: 'custom2', type: 'boolean', list: false },
|
|
815
|
+
{ name: 'custom3', type: 'int', list: false },
|
|
816
|
+
]);
|
|
817
|
+
|
|
818
|
+
const result = addCustomFieldsToFragment(fragmentDocument, {
|
|
819
|
+
customFieldsMap: customFieldsConfig,
|
|
820
|
+
includeCustomFields: ['custom1', 'custom3'],
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
expect(print(result)).toBe(
|
|
824
|
+
normalizeIndentation(`
|
|
825
|
+
fragment Product on Product {
|
|
826
|
+
id
|
|
827
|
+
name
|
|
828
|
+
customFields {
|
|
829
|
+
custom1
|
|
830
|
+
custom3
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
`),
|
|
834
|
+
);
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it('Handles fragment with no custom fields configured', () => {
|
|
838
|
+
const fragmentDocument = graphql(`
|
|
839
|
+
fragment Product on Product {
|
|
840
|
+
id
|
|
841
|
+
name
|
|
842
|
+
}
|
|
843
|
+
`);
|
|
844
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
845
|
+
|
|
846
|
+
const result = addCustomFieldsToFragment(fragmentDocument, {
|
|
847
|
+
customFieldsMap: customFieldsConfig,
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
// Should return the fragment unchanged
|
|
851
|
+
expect(print(result)).toBe(
|
|
852
|
+
normalizeIndentation(`
|
|
853
|
+
fragment Product on Product {
|
|
854
|
+
id
|
|
855
|
+
name
|
|
856
|
+
}
|
|
857
|
+
`),
|
|
858
|
+
);
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
describe('Validation', () => {
|
|
863
|
+
it('Throws error when given a query document', () => {
|
|
864
|
+
const documentNode = graphql(`
|
|
865
|
+
query GetProduct {
|
|
866
|
+
product {
|
|
867
|
+
id
|
|
868
|
+
name
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
`);
|
|
872
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
873
|
+
customFieldsConfig.set('Product', [{ name: 'custom', type: 'string', list: false }]);
|
|
874
|
+
|
|
875
|
+
expect(() =>
|
|
876
|
+
addCustomFieldsToFragment(documentNode, { customFieldsMap: customFieldsConfig }),
|
|
877
|
+
).toThrow('expects a fragment-only document');
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
it('Only modifies the first fragment when multiple fragments are present', () => {
|
|
881
|
+
const productFragment = graphql(`
|
|
882
|
+
fragment Product on Product {
|
|
883
|
+
id
|
|
884
|
+
}
|
|
885
|
+
`);
|
|
886
|
+
const variantFragment = graphql(`
|
|
887
|
+
fragment Variant on ProductVariant {
|
|
888
|
+
id
|
|
889
|
+
}
|
|
890
|
+
`);
|
|
891
|
+
|
|
892
|
+
// Create a document with both fragments (Product first, then Variant)
|
|
893
|
+
const multiFragmentDoc = {
|
|
894
|
+
kind: Kind.DOCUMENT,
|
|
895
|
+
definitions: [...productFragment.definitions, ...variantFragment.definitions],
|
|
896
|
+
} as DocumentNode;
|
|
897
|
+
|
|
898
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
899
|
+
customFieldsConfig.set('Product', [{ name: 'productCustom', type: 'string', list: false }]);
|
|
900
|
+
customFieldsConfig.set('ProductVariant', [
|
|
901
|
+
{ name: 'variantCustom', type: 'string', list: false },
|
|
902
|
+
]);
|
|
903
|
+
|
|
904
|
+
const result = addCustomFieldsToFragment(multiFragmentDoc, {
|
|
905
|
+
customFieldsMap: customFieldsConfig,
|
|
906
|
+
});
|
|
907
|
+
const printed = print(result);
|
|
908
|
+
|
|
909
|
+
// Should add customFields to Product (first fragment)
|
|
910
|
+
expect(printed).toContain('fragment Product on Product');
|
|
911
|
+
expect(printed).toContain('productCustom');
|
|
912
|
+
|
|
913
|
+
// Should NOT add customFields to Variant (dependency fragment)
|
|
914
|
+
expect(printed).toContain('fragment Variant on ProductVariant');
|
|
915
|
+
expect(printed).not.toContain('variantCustom');
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
it('Throws error when given an empty document', () => {
|
|
919
|
+
const emptyDoc = {
|
|
920
|
+
kind: Kind.DOCUMENT,
|
|
921
|
+
definitions: [],
|
|
922
|
+
} as DocumentNode;
|
|
923
|
+
|
|
924
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
925
|
+
|
|
926
|
+
expect(() =>
|
|
927
|
+
addCustomFieldsToFragment(emptyDoc, { customFieldsMap: customFieldsConfig }),
|
|
928
|
+
).toThrow('expects a document with at least one fragment definition');
|
|
929
|
+
});
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
describe('Advanced field types', () => {
|
|
933
|
+
it('Handles relation custom fields', () => {
|
|
934
|
+
const fragmentDocument = graphql(`
|
|
935
|
+
fragment Product on Product {
|
|
936
|
+
id
|
|
937
|
+
name
|
|
938
|
+
}
|
|
939
|
+
`);
|
|
940
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
941
|
+
customFieldsConfig.set('Product', [
|
|
942
|
+
{
|
|
943
|
+
name: 'relatedProduct',
|
|
944
|
+
type: 'relation',
|
|
945
|
+
list: false,
|
|
946
|
+
scalarFields: ['id', 'name', 'slug'],
|
|
947
|
+
},
|
|
948
|
+
]);
|
|
949
|
+
|
|
950
|
+
const result = addCustomFieldsToFragment(fragmentDocument, {
|
|
951
|
+
customFieldsMap: customFieldsConfig,
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
expect(print(result)).toBe(
|
|
955
|
+
normalizeIndentation(`
|
|
956
|
+
fragment Product on Product {
|
|
957
|
+
id
|
|
958
|
+
name
|
|
959
|
+
customFields {
|
|
960
|
+
relatedProduct {
|
|
961
|
+
id
|
|
962
|
+
name
|
|
963
|
+
slug
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
`),
|
|
968
|
+
);
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
it('Handles struct custom fields', () => {
|
|
972
|
+
const fragmentDocument = graphql(`
|
|
973
|
+
fragment Product on Product {
|
|
974
|
+
id
|
|
975
|
+
name
|
|
976
|
+
}
|
|
977
|
+
`);
|
|
978
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
979
|
+
customFieldsConfig.set('Product', [
|
|
980
|
+
{
|
|
981
|
+
name: 'dimensions',
|
|
982
|
+
type: 'struct',
|
|
983
|
+
list: false,
|
|
984
|
+
fields: [
|
|
985
|
+
{ name: 'width', type: 'int' },
|
|
986
|
+
{ name: 'height', type: 'int' },
|
|
987
|
+
{ name: 'depth', type: 'int' },
|
|
988
|
+
],
|
|
989
|
+
},
|
|
990
|
+
]);
|
|
991
|
+
|
|
992
|
+
const result = addCustomFieldsToFragment(fragmentDocument, {
|
|
993
|
+
customFieldsMap: customFieldsConfig,
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
expect(print(result)).toBe(
|
|
997
|
+
normalizeIndentation(`
|
|
998
|
+
fragment Product on Product {
|
|
999
|
+
id
|
|
1000
|
+
name
|
|
1001
|
+
customFields {
|
|
1002
|
+
dimensions {
|
|
1003
|
+
width
|
|
1004
|
+
height
|
|
1005
|
+
depth
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
`),
|
|
1010
|
+
);
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
it('Handles localized custom fields in translations', () => {
|
|
1014
|
+
const fragmentDocument = graphql(`
|
|
1015
|
+
fragment Product on Product {
|
|
1016
|
+
id
|
|
1017
|
+
translations {
|
|
1018
|
+
languageCode
|
|
1019
|
+
name
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
`);
|
|
1023
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1024
|
+
customFieldsConfig.set('Product', [
|
|
1025
|
+
{ name: 'customDescription', type: 'localeString', list: false },
|
|
1026
|
+
{ name: 'customSeoTitle', type: 'localeText', list: false },
|
|
1027
|
+
]);
|
|
1028
|
+
|
|
1029
|
+
const result = addCustomFieldsToFragment(fragmentDocument, {
|
|
1030
|
+
customFieldsMap: customFieldsConfig,
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
const printed = print(result);
|
|
1034
|
+
// Should add localized fields to translations
|
|
1035
|
+
expect(printed).toContain('translations {');
|
|
1036
|
+
expect(printed).toMatch(/translations\s*\{[^}]*customFields/s);
|
|
1037
|
+
|
|
1038
|
+
const fragmentDef = result.definitions[0] as FragmentDefinitionNode;
|
|
1039
|
+
const translationsField = fragmentDef.selectionSet.selections.find(
|
|
1040
|
+
s => s.kind === Kind.FIELD && s.name.value === 'translations',
|
|
1041
|
+
) as FieldNode;
|
|
1042
|
+
const customFieldsInTranslations = translationsField.selectionSet!.selections.find(
|
|
1043
|
+
s => s.kind === Kind.FIELD && s.name.value === 'customFields',
|
|
1044
|
+
) as FieldNode;
|
|
1045
|
+
|
|
1046
|
+
expect(customFieldsInTranslations).toBeTruthy();
|
|
1047
|
+
expect(customFieldsInTranslations.selectionSet!.selections.length).toBe(2);
|
|
1048
|
+
});
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
describe('Special type handling', () => {
|
|
1052
|
+
it('Handles OrderAddress as alias of Address', () => {
|
|
1053
|
+
const fragmentDocument = graphql(`
|
|
1054
|
+
fragment OrderAddress on OrderAddress {
|
|
1055
|
+
id
|
|
1056
|
+
streetLine1
|
|
1057
|
+
}
|
|
1058
|
+
`);
|
|
1059
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1060
|
+
// Custom fields are configured for Address, not OrderAddress
|
|
1061
|
+
customFieldsConfig.set('Address', [{ name: 'buildingNumber', type: 'string', list: false }]);
|
|
1062
|
+
|
|
1063
|
+
const result = addCustomFieldsToFragment(fragmentDocument, {
|
|
1064
|
+
customFieldsMap: customFieldsConfig,
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
// Should still add custom fields because OrderAddress is aliased to Address
|
|
1068
|
+
expect(print(result)).toContain('customFields {');
|
|
1069
|
+
expect(print(result)).toContain('buildingNumber');
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
it('Handles Country as alias of Region', () => {
|
|
1073
|
+
const fragmentDocument = graphql(`
|
|
1074
|
+
fragment Country on Country {
|
|
1075
|
+
id
|
|
1076
|
+
name
|
|
1077
|
+
}
|
|
1078
|
+
`);
|
|
1079
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1080
|
+
// Custom fields are configured for Region, not Country
|
|
1081
|
+
customFieldsConfig.set('Region', [{ name: 'regionCode', type: 'string', list: false }]);
|
|
1082
|
+
|
|
1083
|
+
const result = addCustomFieldsToFragment(fragmentDocument, {
|
|
1084
|
+
customFieldsMap: customFieldsConfig,
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
// Should still add custom fields because Country is aliased to Region
|
|
1088
|
+
expect(print(result)).toContain('customFields {');
|
|
1089
|
+
expect(print(result)).toContain('regionCode');
|
|
1090
|
+
});
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
describe('Memoization', () => {
|
|
1094
|
+
it('Returns the same instance for the same inputs', () => {
|
|
1095
|
+
const fragmentDocument = graphql(`
|
|
1096
|
+
fragment Product on Product {
|
|
1097
|
+
id
|
|
1098
|
+
name
|
|
1099
|
+
}
|
|
1100
|
+
`);
|
|
1101
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1102
|
+
customFieldsConfig.set('Product', [{ name: 'custom', type: 'string', list: false }]);
|
|
1103
|
+
|
|
1104
|
+
const result1 = addCustomFieldsToFragment(fragmentDocument, {
|
|
1105
|
+
customFieldsMap: customFieldsConfig,
|
|
1106
|
+
});
|
|
1107
|
+
const result2 = addCustomFieldsToFragment(fragmentDocument, {
|
|
1108
|
+
customFieldsMap: customFieldsConfig,
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
// Should return the exact same instance (identity equality)
|
|
1112
|
+
expect(result1).toBe(result2);
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
it('Returns different instances for different options', () => {
|
|
1116
|
+
const fragmentDocument = graphql(`
|
|
1117
|
+
fragment Product on Product {
|
|
1118
|
+
id
|
|
1119
|
+
name
|
|
1120
|
+
}
|
|
1121
|
+
`);
|
|
1122
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1123
|
+
customFieldsConfig.set('Product', [
|
|
1124
|
+
{ name: 'custom1', type: 'string', list: false },
|
|
1125
|
+
{ name: 'custom2', type: 'boolean', list: false },
|
|
1126
|
+
]);
|
|
1127
|
+
|
|
1128
|
+
const result1 = addCustomFieldsToFragment(fragmentDocument, {
|
|
1129
|
+
customFieldsMap: customFieldsConfig,
|
|
1130
|
+
includeCustomFields: ['custom1'],
|
|
1131
|
+
});
|
|
1132
|
+
const result2 = addCustomFieldsToFragment(fragmentDocument, {
|
|
1133
|
+
customFieldsMap: customFieldsConfig,
|
|
1134
|
+
includeCustomFields: ['custom2'],
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
// Should return different instances for different options
|
|
1138
|
+
expect(result1).not.toBe(result2);
|
|
1139
|
+
expect(print(result1)).toContain('custom1');
|
|
1140
|
+
expect(print(result1)).not.toContain('custom2');
|
|
1141
|
+
expect(print(result2)).toContain('custom2');
|
|
1142
|
+
expect(print(result2)).not.toContain('custom1');
|
|
1143
|
+
});
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
describe('Fragment spreads handling', () => {
|
|
1147
|
+
it('Should only add custom fields to the top-level fragment, not to referenced fragments', () => {
|
|
1148
|
+
const orderLineFragment = graphql(`
|
|
1149
|
+
fragment OrderLine on OrderLine {
|
|
1150
|
+
id
|
|
1151
|
+
quantity
|
|
1152
|
+
}
|
|
1153
|
+
`);
|
|
1154
|
+
|
|
1155
|
+
const orderDetailFragment = graphql(
|
|
1156
|
+
`
|
|
1157
|
+
fragment OrderDetail on Order {
|
|
1158
|
+
id
|
|
1159
|
+
code
|
|
1160
|
+
lines {
|
|
1161
|
+
...OrderLine
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
`,
|
|
1165
|
+
[orderLineFragment],
|
|
1166
|
+
);
|
|
1167
|
+
|
|
1168
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1169
|
+
customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
|
|
1170
|
+
customFieldsConfig.set('OrderLine', [
|
|
1171
|
+
{ name: 'orderLineCustomField', type: 'string', list: false },
|
|
1172
|
+
]);
|
|
1173
|
+
|
|
1174
|
+
// Apply to the OrderDetail fragment only
|
|
1175
|
+
const result = addCustomFieldsToFragment(orderDetailFragment, {
|
|
1176
|
+
customFieldsMap: customFieldsConfig,
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
const printed = print(result);
|
|
1180
|
+
|
|
1181
|
+
// Should add customFields to OrderDetail (top-level fragment)
|
|
1182
|
+
expect(printed).toContain('fragment OrderDetail on Order');
|
|
1183
|
+
expect(printed).toContain('orderCustomField');
|
|
1184
|
+
|
|
1185
|
+
// Should include the OrderLine fragment definition (dependency) but NOT add customFields to it
|
|
1186
|
+
expect(printed).toContain('...OrderLine');
|
|
1187
|
+
expect(printed).toContain('fragment OrderLine on OrderLine');
|
|
1188
|
+
expect(printed).not.toContain('orderLineCustomField');
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
it('Should work with deeply nested fragment spreads', () => {
|
|
1192
|
+
const assetFragment = graphql(`
|
|
1193
|
+
fragment Asset on Asset {
|
|
1194
|
+
id
|
|
1195
|
+
preview
|
|
1196
|
+
}
|
|
1197
|
+
`);
|
|
1198
|
+
|
|
1199
|
+
const orderLineFragment = graphql(
|
|
1200
|
+
`
|
|
1201
|
+
fragment OrderLine on OrderLine {
|
|
1202
|
+
id
|
|
1203
|
+
quantity
|
|
1204
|
+
featuredAsset {
|
|
1205
|
+
...Asset
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
`,
|
|
1209
|
+
[assetFragment],
|
|
1210
|
+
);
|
|
1211
|
+
|
|
1212
|
+
const orderDetailFragment = graphql(
|
|
1213
|
+
`
|
|
1214
|
+
fragment OrderDetail on Order {
|
|
1215
|
+
id
|
|
1216
|
+
code
|
|
1217
|
+
lines {
|
|
1218
|
+
...OrderLine
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
`,
|
|
1222
|
+
[orderLineFragment],
|
|
1223
|
+
);
|
|
1224
|
+
|
|
1225
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1226
|
+
customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
|
|
1227
|
+
customFieldsConfig.set('OrderLine', [
|
|
1228
|
+
{ name: 'orderLineCustomField', type: 'string', list: false },
|
|
1229
|
+
]);
|
|
1230
|
+
customFieldsConfig.set('Asset', [{ name: 'assetCustomField', type: 'string', list: false }]);
|
|
1231
|
+
|
|
1232
|
+
const result = addCustomFieldsToFragment(orderDetailFragment, {
|
|
1233
|
+
customFieldsMap: customFieldsConfig,
|
|
1234
|
+
});
|
|
1235
|
+
|
|
1236
|
+
const printed = print(result);
|
|
1237
|
+
|
|
1238
|
+
// Should ONLY add customFields to OrderDetail
|
|
1239
|
+
expect(printed).toContain('orderCustomField');
|
|
1240
|
+
expect(printed).not.toContain('orderLineCustomField');
|
|
1241
|
+
expect(printed).not.toContain('assetCustomField');
|
|
1242
|
+
|
|
1243
|
+
// Should still contain the fragment definitions (dependencies) but without custom fields
|
|
1244
|
+
expect(printed).toContain('...OrderLine');
|
|
1245
|
+
expect(printed).toContain('fragment OrderLine on OrderLine');
|
|
1246
|
+
expect(printed).toContain('fragment Asset on Asset');
|
|
1247
|
+
});
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1250
|
+
describe('Composability with addCustomFields()', () => {
|
|
1251
|
+
it('Can be used inline in graphql() dependency array (like the original pattern)', () => {
|
|
1252
|
+
const productFragment = graphql(`
|
|
1253
|
+
fragment Product on Product {
|
|
1254
|
+
id
|
|
1255
|
+
name
|
|
1256
|
+
}
|
|
1257
|
+
`);
|
|
1258
|
+
|
|
1259
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1260
|
+
customFieldsConfig.set('Product', [
|
|
1261
|
+
{ name: 'custom1', type: 'string', list: false },
|
|
1262
|
+
{ name: 'custom2', type: 'boolean', list: false },
|
|
1263
|
+
]);
|
|
1264
|
+
|
|
1265
|
+
// Use addCustomFieldsToFragment directly in the array - this is the pattern from orders.graphql.ts
|
|
1266
|
+
const queryDocument = graphql(
|
|
1267
|
+
`
|
|
1268
|
+
query GetProduct {
|
|
1269
|
+
product {
|
|
1270
|
+
...Product
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
`,
|
|
1274
|
+
[addCustomFieldsToFragment(productFragment, { customFieldsMap: customFieldsConfig })],
|
|
1275
|
+
);
|
|
1276
|
+
|
|
1277
|
+
// The query should include the modified fragment with custom fields
|
|
1278
|
+
const printed = print(queryDocument);
|
|
1279
|
+
expect(printed).toContain('customFields {');
|
|
1280
|
+
expect(printed).toContain('custom1');
|
|
1281
|
+
expect(printed).toContain('custom2');
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
it('addCustomFieldsToFragment produces same result as addCustomFields for single fragments', () => {
|
|
1285
|
+
const productFragment = graphql(`
|
|
1286
|
+
fragment Product on Product {
|
|
1287
|
+
id
|
|
1288
|
+
name
|
|
1289
|
+
}
|
|
1290
|
+
`);
|
|
1291
|
+
|
|
1292
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1293
|
+
customFieldsConfig.set('Product', [
|
|
1294
|
+
{ name: 'custom1', type: 'string', list: false },
|
|
1295
|
+
{ name: 'custom2', type: 'boolean', list: false },
|
|
1296
|
+
]);
|
|
1297
|
+
|
|
1298
|
+
const resultFromFragment = addCustomFieldsToFragment(productFragment, {
|
|
1299
|
+
customFieldsMap: customFieldsConfig,
|
|
1300
|
+
});
|
|
1301
|
+
const resultFromFull = addCustomFields(productFragment, { customFieldsMap: customFieldsConfig });
|
|
1302
|
+
|
|
1303
|
+
// Both should produce the same output
|
|
1304
|
+
expect(print(resultFromFragment)).toBe(print(resultFromFull));
|
|
1305
|
+
});
|
|
1306
|
+
|
|
1307
|
+
it('Works with fragments that have dependencies when used inline', () => {
|
|
1308
|
+
const orderLineFragment = graphql(`
|
|
1309
|
+
fragment OrderLine on OrderLine {
|
|
1310
|
+
id
|
|
1311
|
+
quantity
|
|
1312
|
+
}
|
|
1313
|
+
`);
|
|
1314
|
+
|
|
1315
|
+
const orderDetailFragment = graphql(
|
|
1316
|
+
`
|
|
1317
|
+
fragment OrderDetail on Order {
|
|
1318
|
+
id
|
|
1319
|
+
code
|
|
1320
|
+
lines {
|
|
1321
|
+
...OrderLine
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
`,
|
|
1325
|
+
[orderLineFragment],
|
|
1326
|
+
);
|
|
1327
|
+
|
|
1328
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1329
|
+
customFieldsConfig.set('Order', [{ name: 'orderCustomField', type: 'string', list: false }]);
|
|
1330
|
+
customFieldsConfig.set('OrderLine', [
|
|
1331
|
+
{ name: 'orderLineCustomField', type: 'string', list: false },
|
|
1332
|
+
]);
|
|
1333
|
+
|
|
1334
|
+
// This is exactly the pattern used in orders.graphql.ts
|
|
1335
|
+
const queryDocument = graphql(
|
|
1336
|
+
`
|
|
1337
|
+
query GetOrder($id: ID!) {
|
|
1338
|
+
order(id: $id) {
|
|
1339
|
+
...OrderDetail
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
`,
|
|
1343
|
+
[addCustomFieldsToFragment(orderDetailFragment, { customFieldsMap: customFieldsConfig })],
|
|
1344
|
+
);
|
|
1345
|
+
|
|
1346
|
+
const printed = print(queryDocument);
|
|
1347
|
+
|
|
1348
|
+
// Should add custom fields to OrderDetail
|
|
1349
|
+
expect(printed).toContain('fragment OrderDetail on Order');
|
|
1350
|
+
expect(printed).toContain('orderCustomField');
|
|
1351
|
+
|
|
1352
|
+
// Should NOT add custom fields to OrderLine (dependency)
|
|
1353
|
+
expect(printed).toContain('fragment OrderLine on OrderLine');
|
|
1354
|
+
expect(printed).not.toContain('orderLineCustomField');
|
|
1355
|
+
|
|
1356
|
+
// Verify the query structure is correct
|
|
1357
|
+
expect(printed).toContain('query GetOrder');
|
|
1358
|
+
expect(printed).toContain('order(id: $id)');
|
|
1359
|
+
expect(printed).toContain('...OrderDetail');
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
it('Can be used to compose fragments in query documents', () => {
|
|
1363
|
+
const productFragment = graphql(`
|
|
1364
|
+
fragment Product on Product {
|
|
1365
|
+
id
|
|
1366
|
+
name
|
|
1367
|
+
}
|
|
1368
|
+
`);
|
|
1369
|
+
|
|
1370
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1371
|
+
customFieldsConfig.set('Product', [
|
|
1372
|
+
{ name: 'custom1', type: 'string', list: false },
|
|
1373
|
+
{ name: 'custom2', type: 'boolean', list: false },
|
|
1374
|
+
]);
|
|
1375
|
+
|
|
1376
|
+
// Use addCustomFieldsToFragment to modify the fragment
|
|
1377
|
+
const modifiedFragment = addCustomFieldsToFragment(productFragment, {
|
|
1378
|
+
customFieldsMap: customFieldsConfig,
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
// Then compose it into a query
|
|
1382
|
+
const queryDocument = graphql(
|
|
1383
|
+
`
|
|
1384
|
+
query GetProduct {
|
|
1385
|
+
product {
|
|
1386
|
+
...Product
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
`,
|
|
1390
|
+
[modifiedFragment],
|
|
1391
|
+
);
|
|
1392
|
+
|
|
1393
|
+
// The query should include the modified fragment with custom fields
|
|
1394
|
+
const printed = print(queryDocument);
|
|
1395
|
+
expect(printed).toContain('customFields {');
|
|
1396
|
+
expect(printed).toContain('custom1');
|
|
1397
|
+
expect(printed).toContain('custom2');
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
it('Can selectively modify different fragments with different custom fields', () => {
|
|
1401
|
+
const productFragment = graphql(`
|
|
1402
|
+
fragment Product on Product {
|
|
1403
|
+
id
|
|
1404
|
+
name
|
|
1405
|
+
}
|
|
1406
|
+
`);
|
|
1407
|
+
|
|
1408
|
+
const variantFragment = graphql(`
|
|
1409
|
+
fragment Variant on ProductVariant {
|
|
1410
|
+
id
|
|
1411
|
+
sku
|
|
1412
|
+
}
|
|
1413
|
+
`);
|
|
1414
|
+
|
|
1415
|
+
const customFieldsConfig = new Map<string, CustomFieldConfig[]>();
|
|
1416
|
+
customFieldsConfig.set('Product', [
|
|
1417
|
+
{ name: 'productCustom1', type: 'string', list: false },
|
|
1418
|
+
{ name: 'productCustom2', type: 'boolean', list: false },
|
|
1419
|
+
]);
|
|
1420
|
+
customFieldsConfig.set('ProductVariant', [
|
|
1421
|
+
{ name: 'variantCustom1', type: 'string', list: false },
|
|
1422
|
+
]);
|
|
1423
|
+
|
|
1424
|
+
// Selectively modify each fragment with different custom fields
|
|
1425
|
+
const modifiedProductFragment = addCustomFieldsToFragment(productFragment, {
|
|
1426
|
+
customFieldsMap: customFieldsConfig,
|
|
1427
|
+
includeCustomFields: ['productCustom1'], // Only include productCustom1
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
const modifiedVariantFragment = addCustomFieldsToFragment(variantFragment, {
|
|
1431
|
+
customFieldsMap: customFieldsConfig,
|
|
1432
|
+
includeCustomFields: ['variantCustom1'],
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
// Compose into a query
|
|
1436
|
+
const queryDocument = graphql(
|
|
1437
|
+
`
|
|
1438
|
+
query GetProductWithVariants {
|
|
1439
|
+
product {
|
|
1440
|
+
...Product
|
|
1441
|
+
variants {
|
|
1442
|
+
...Variant
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
`,
|
|
1447
|
+
[modifiedProductFragment, modifiedVariantFragment],
|
|
1448
|
+
);
|
|
1449
|
+
|
|
1450
|
+
const printed = print(queryDocument);
|
|
1451
|
+
// Product fragment should have only productCustom1
|
|
1452
|
+
expect(printed).toContain('productCustom1');
|
|
1453
|
+
expect(printed).not.toContain('productCustom2');
|
|
1454
|
+
// Variant fragment should have variantCustom1
|
|
1455
|
+
expect(printed).toContain('variantCustom1');
|
|
1456
|
+
});
|
|
1457
|
+
});
|
|
1458
|
+
});
|