Copy filters automatically to GraphiQL playground (#3385)

This commit is contained in:
Jakub Neander 2023-03-23 15:53:48 +01:00 committed by GitHub
parent 9690313d16
commit 82d15f4441
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 287 additions and 45 deletions

View file

@ -26,6 +26,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Add DevMode panel (trigger: CMD+') - #3333 by @zaiste
- Migrate to `es2020` for TypeScript - #3386 by @zaiste
- Fix styling for GraphiQL on the webhook page - #3389 by @zaiste
- Copy filters automatically to GraphiQL playground - #3385 by @zaiste
## 3.4

View file

@ -8010,6 +8010,10 @@
"vEYtiq": {
"string": "Category Name"
},
"vEwjub": {
"context": "button",
"string": "Open in GraphiQL"
},
"vM9quW": {
"context": "order payment",
"string": "Paid with Gift Card"

View file

@ -1,8 +1,11 @@
import useAppState from "@dashboard/hooks/useAppState";
import { DevModeQuery } from "@dashboard/orders/queries";
import { getFilterVariables } from "@dashboard/orders/views/OrderList/filters";
import { LinearProgress } from "@material-ui/core";
import { useActionBar } from "@saleor/macaw-ui";
import { Box } from "@saleor/macaw-ui/next";
import React, { useState } from "react";
import { useLocation } from "react-router";
import { DevModePanel } from "../DevModePanel/DevModePanel";
import { useDevModeContext } from "../DevModePanel/hooks";
@ -11,6 +14,7 @@ import Navigator from "../Navigator";
import { Sidebar } from "../Sidebar";
import { contentMaxWidth } from "./consts";
import { useStyles } from "./styles";
import { extractQueryParams } from "./util";
interface AppLayoutProps {
children: React.ReactNode;
@ -23,9 +27,32 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
const [appState] = useAppState();
const [isNavigatorVisible, setNavigatorVisibility] = useState(false);
const { isDevModeVisible, setDevModeVisibility } = useDevModeContext();
const {
isDevModeVisible,
setDevModeVisibility,
setDevModeContent,
setVariables,
} = useDevModeContext();
useDevModeKeyTrigger(() => setDevModeVisibility(!isDevModeVisible));
const params = extractQueryParams(useLocation().search);
useDevModeKeyTrigger(({ shift }) => {
if (shift) {
setDevModeContent(DevModeQuery);
const variables = JSON.stringify(
{
filter: getFilterVariables(params),
},
null,
2,
);
setVariables(variables);
} else {
setDevModeContent("");
setVariables("");
}
setDevModeVisibility(!isDevModeVisible);
});
return (
<>

View file

@ -0,0 +1,25 @@
import { extractQueryParams } from "./util";
describe("extractQueryParams", () => {
test("parses a query string with an explicitly indexed list into an object of key-value pairs", () => {
const queryString =
"q=apple&color[0]=red&color[1]=green&shape[]=round&shape[]=oval";
const expected = {
q: "apple",
color: ["red", "green"],
shape: ["round", "oval"],
};
expect(extractQueryParams(queryString)).toEqual(expected);
});
test("parses a query string into an object of key-value pairs (overwrites non array elements!)", () => {
const queryString =
"q=apple&color=red&color=green&shape[]=round&shape[]=oval";
const expected = {
q: "apple",
color: "green",
shape: ["round", "oval"],
};
expect(extractQueryParams(queryString)).toEqual(expected);
});
});

View file

@ -0,0 +1,23 @@
export const extractQueryParams = (queryString: string) => {
const urlSearchParams = new URLSearchParams(queryString);
const queryParams = {};
urlSearchParams.forEach((value, key) => {
const arrayKeyRegex = /^(.+)\[\d*\]$/;
const match = key.match(arrayKeyRegex);
if (match) {
const arrayKey = match[1];
if (!queryParams.hasOwnProperty(arrayKey)) {
queryParams[arrayKey] = [];
}
queryParams[arrayKey].push(value);
} else {
queryParams[key] = value;
}
});
return queryParams;
};

View file

@ -1,10 +1,14 @@
import { useEffect } from "react";
export const useDevModeKeyTrigger = (callback?: () => void) => {
type DevModeKeyTriggerCallback = ({ shift }: { shift: boolean }) => void;
export const useDevModeKeyTrigger = (callback?: DevModeKeyTriggerCallback) => {
useEffect(() => {
const handler = (event: KeyboardEvent) => {
if (event.metaKey && event.code === "Quote") {
callback();
if (event.shiftKey && event.metaKey && event.code === "Quote") {
callback({ shift: true });
} else if (event.metaKey && event.code === "Quote") {
callback({ shift: false });
}
};

View file

@ -9993,6 +9993,99 @@ export function useChannelUsabilityDataLazyQuery(baseOptions?: ApolloReactHooks.
export type ChannelUsabilityDataQueryHookResult = ReturnType<typeof useChannelUsabilityDataQuery>;
export type ChannelUsabilityDataLazyQueryHookResult = ReturnType<typeof useChannelUsabilityDataLazyQuery>;
export type ChannelUsabilityDataQueryResult = Apollo.QueryResult<Types.ChannelUsabilityDataQuery, Types.ChannelUsabilityDataQueryVariables>;
export const OrderDetailsGraphiQlDocument = gql`
query OrderDetailsGraphiQL($id: ID!) {
order(id: $id) {
id
number
status
isShippingRequired
canFinalize
created
customerNote
paymentStatus
userEmail
isPaid
}
}
`;
/**
* __useOrderDetailsGraphiQlQuery__
*
* To run a query within a React component, call `useOrderDetailsGraphiQlQuery` and pass it any options that fit your needs.
* When your component renders, `useOrderDetailsGraphiQlQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useOrderDetailsGraphiQlQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useOrderDetailsGraphiQlQuery(baseOptions: ApolloReactHooks.QueryHookOptions<Types.OrderDetailsGraphiQlQuery, Types.OrderDetailsGraphiQlQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useQuery<Types.OrderDetailsGraphiQlQuery, Types.OrderDetailsGraphiQlQueryVariables>(OrderDetailsGraphiQlDocument, options);
}
export function useOrderDetailsGraphiQlLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.OrderDetailsGraphiQlQuery, Types.OrderDetailsGraphiQlQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useLazyQuery<Types.OrderDetailsGraphiQlQuery, Types.OrderDetailsGraphiQlQueryVariables>(OrderDetailsGraphiQlDocument, options);
}
export type OrderDetailsGraphiQlQueryHookResult = ReturnType<typeof useOrderDetailsGraphiQlQuery>;
export type OrderDetailsGraphiQlLazyQueryHookResult = ReturnType<typeof useOrderDetailsGraphiQlLazyQuery>;
export type OrderDetailsGraphiQlQueryResult = Apollo.QueryResult<Types.OrderDetailsGraphiQlQuery, Types.OrderDetailsGraphiQlQueryVariables>;
export const DevModeRunDocument = gql`
query DevModeRun($filter: OrderFilterInput, $sortBy: OrderSortingInput) {
orders(first: 10, filter: $filter, sortBy: $sortBy) {
edges {
node {
id
number
status
isShippingRequired
canFinalize
created
customerNote
paymentStatus
userEmail
isPaid
}
}
}
}
`;
/**
* __useDevModeRunQuery__
*
* To run a query within a React component, call `useDevModeRunQuery` and pass it any options that fit your needs.
* When your component renders, `useDevModeRunQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useDevModeRunQuery({
* variables: {
* filter: // value for 'filter'
* sortBy: // value for 'sortBy'
* },
* });
*/
export function useDevModeRunQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<Types.DevModeRunQuery, Types.DevModeRunQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useQuery<Types.DevModeRunQuery, Types.DevModeRunQueryVariables>(DevModeRunDocument, options);
}
export function useDevModeRunLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<Types.DevModeRunQuery, Types.DevModeRunQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return ApolloReactHooks.useLazyQuery<Types.DevModeRunQuery, Types.DevModeRunQueryVariables>(DevModeRunDocument, options);
}
export type DevModeRunQueryHookResult = ReturnType<typeof useDevModeRunQuery>;
export type DevModeRunLazyQueryHookResult = ReturnType<typeof useDevModeRunLazyQuery>;
export type DevModeRunQueryResult = Apollo.QueryResult<Types.DevModeRunQuery, Types.DevModeRunQueryVariables>;
export const PageTypeUpdateDocument = gql`
mutation PageTypeUpdate($id: ID!, $input: PageTypeUpdateInput!) {
pageTypeUpdate(id: $id, input: $input) {

View file

@ -8640,6 +8640,21 @@ export type ChannelUsabilityDataQueryVariables = Exact<{
export type ChannelUsabilityDataQuery = { __typename: 'Query', products: { __typename: 'ProductCountableConnection', totalCount: number | null } | null };
export type OrderDetailsGraphiQlQueryVariables = Exact<{
id: Scalars['ID'];
}>;
export type OrderDetailsGraphiQlQuery = { __typename: 'Query', order: { __typename: 'Order', id: string, number: string, status: OrderStatus, isShippingRequired: boolean, canFinalize: boolean, created: any, customerNote: string, paymentStatus: PaymentChargeStatusEnum, userEmail: string | null, isPaid: boolean } | null };
export type DevModeRunQueryVariables = Exact<{
filter?: InputMaybe<OrderFilterInput>;
sortBy?: InputMaybe<OrderSortingInput>;
}>;
export type DevModeRunQuery = { __typename: 'Query', orders: { __typename: 'OrderCountableConnection', edges: Array<{ __typename: 'OrderCountableEdge', node: { __typename: 'Order', id: string, number: string, status: OrderStatus, isShippingRequired: boolean, canFinalize: boolean, created: any, customerNote: string, paymentStatus: PaymentChargeStatusEnum, userEmail: string | null, isPaid: boolean } }> } | null };
export type PageTypeUpdateMutationVariables = Exact<{
id: Scalars['ID'];
input: PageTypeUpdateInput;

View file

@ -78,6 +78,7 @@ const props: OrderListPageProps = {
...sortPageProps.sort,
sort: OrderListUrlSortField.number,
},
params: {},
};
storiesOf("Orders / Order list", module)

View file

@ -7,11 +7,17 @@ import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo";
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
import { ButtonWithSelect } from "@dashboard/components/ButtonWithSelect";
import CardMenu from "@dashboard/components/CardMenu";
import { useDevModeContext } from "@dashboard/components/DevModePanel/hooks";
import FilterBar from "@dashboard/components/FilterBar";
import { ListPageLayout } from "@dashboard/components/Layouts";
import { OrderListQuery, RefreshLimitsQuery } from "@dashboard/graphql";
import { sectionNames } from "@dashboard/intl";
import { OrderListUrlSortField } from "@dashboard/orders/urls";
import { DevModeQuery } from "@dashboard/orders/queries";
import {
OrderListUrlQueryParams,
OrderListUrlSortField,
} from "@dashboard/orders/urls";
import { getFilterVariables } from "@dashboard/orders/views/OrderList/filters";
import {
FilterPageProps,
PageListProps,
@ -40,6 +46,7 @@ export interface OrderListPageProps
orders: RelayToFlat<OrderListQuery["orders"]>;
onSettingsOpen: () => void;
onAdd: () => void;
params: OrderListUrlQueryParams;
}
const useStyles = makeStyles(
@ -65,6 +72,7 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
onTabChange,
onTabDelete,
onTabSave,
params,
...listProps
}) => {
const intl = useIntl();
@ -78,6 +86,24 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
const extensionMenuItems = mapToMenuItems(ORDER_OVERVIEW_MORE_ACTIONS);
const extensionCreateButtonItems = mapToMenuItems(ORDER_OVERVIEW_CREATE);
const context = useDevModeContext();
const openPlaygroundURL = () => {
context.setDevModeContent(DevModeQuery);
const variables = JSON.stringify(
{
filter: getFilterVariables(params),
// TODO add sorting: Issue #3409
// strange error when uncommenting this line
// sortBy: getSortQueryVariables(params)
},
null,
2,
);
context.setVariables(variables);
context.setDevModeVisibility(true);
};
return (
<ListPageLayout>
<TopNav title={intl.formatMessage(sectionNames.orders)}>
@ -85,6 +111,14 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
<CardMenu
className={classes.settings}
menuItems={[
{
label: intl.formatMessage({
id: "vEwjub",
defaultMessage: "Open in GraphiQL",
description: "button",
}),
onSelect: openPlaygroundURL,
},
{
label: intl.formatMessage({
id: "WbV1Xm",

View file

@ -201,17 +201,40 @@ export const channelUsabilityData = gql`
}
`;
export const defaultGraphiQLQuery = `query OrderDetails($id: ID!) {
order(id: $id) {
id
number
status
isShippingRequired
canFinalize
created
customerNote
paymentStatus
userEmail
isPaid
export const defaultGraphiQLQuery = /* GraphQL */ `
query OrderDetailsGraphiQL($id: ID!) {
order(id: $id) {
id
number
status
isShippingRequired
canFinalize
created
customerNote
paymentStatus
userEmail
isPaid
}
}
}`;
`;
export const DevModeQuery = /* GraphQL */ `
query DevModeRun($filter: OrderFilterInput, $sortBy: OrderSortingInput) {
orders(first: 10, filter: $filter, sortBy: $sortBy) {
edges {
node {
id
number
status
isShippingRequired
canFinalize
created
customerNote
paymentStatus
userEmail
isPaid
}
}
}
}
`;

View file

@ -93,16 +93,13 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
const currentTab = getFiltersCurrentTab(params, tabs);
const [
changeFilters,
resetFilters,
handleSearchChange,
] = createFilterHandlers({
createUrl: orderListUrl,
getFilterQueryParam,
navigate,
params,
});
const [changeFilters, resetFilters, handleSearchChange] =
createFilterHandlers({
createUrl: orderListUrl,
getFilterQueryParam,
navigate,
params,
});
const [openModal, closeModal] = createDialogActionHandlers<
OrderListUrlDialog,
@ -178,6 +175,7 @@ export const OrderList: React.FC<OrderListProps> = ({ params }) => {
tabs={getFilterTabs().map(tab => tab.name)}
onAll={resetFilters}
onSettingsOpen={() => navigate(orderSettingsPath)}
params={params}
/>
<SaveFilterTabDialog
open={params.action === "save-search"}

View file

@ -108,7 +108,7 @@ export function getFilterVariables(
params: OrderListUrlFilters,
): OrderFilterInput {
return {
channels: (params.channel as unknown) as string[],
channels: params.channel as unknown as string[],
created: getGteLteVariables({
gte: params.createdFrom,
lte: params.createdTo,
@ -198,18 +198,12 @@ export function getFilterQueryParam(
}
}
export const {
deleteFilterTab,
getFilterTabs,
saveFilterTab,
} = createFilterTabUtils<OrderListUrlFilters>(ORDER_FILTERS_KEY);
export const { deleteFilterTab, getFilterTabs, saveFilterTab } =
createFilterTabUtils<OrderListUrlFilters>(ORDER_FILTERS_KEY);
export const {
areFiltersApplied,
getActiveFilters,
getFiltersCurrentTab,
} = createFilterUtils<OrderListUrlQueryParams, OrderListUrlFilters>({
...OrderListUrlFiltersEnum,
...OrderListUrlFiltersWithMultipleValues,
...OrderListFitersWithKeyValueValues,
});
export const { areFiltersApplied, getActiveFilters, getFiltersCurrentTab } =
createFilterUtils<OrderListUrlQueryParams, OrderListUrlFilters>({
...OrderListUrlFiltersEnum,
...OrderListUrlFiltersWithMultipleValues,
...OrderListFitersWithKeyValueValues,
});