console: fix storybook performance and stability issues due to console type addon

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/10007
GitOrigin-RevId: d78a161f7f5696162409c2757c594fa0c74cfd1c
This commit is contained in:
Matthew Goodwin 2023-08-04 11:38:42 -05:00 committed by hasura-bot
parent 82c7797dbb
commit 603fb42def
32 changed files with 496 additions and 253 deletions

View File

@ -5,7 +5,6 @@ export default {
'@storybook/addon-links',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
'storybook-addon-console-env',
'@storybook/addon-mdx-gfm',
],
webpackFinal: async (config: any) => {

View File

@ -46,4 +46,16 @@
<script language="JavaScript">
window.preventJsonWebTokenLoad = true;
// initialize the window.__env object
window.__env = {
dataApiUrl: 'http://localhost:8080',
apiHost: 'http://localhost',
apiPort: '8080',
nodeEnv: 'development',
consoleType: 'oss',
adminSecretSet: false,
adminSecret: null,
tenantID: null,
};
</script>

View File

@ -9,6 +9,7 @@ import { stripTrailingSlash } from './components/Common/utils/urlUtils';
import { SERVER_CONSOLE_MODE } from './constants';
import { ConsoleType, parseConsoleType } from './utils/envUtils';
import { storybookGlobals } from './storybook/decorators/console-type/storybook-globals';
export type LuxFeature =
| 'DatadogIntegration'
@ -272,4 +273,19 @@ if (globals.consoleMode === SERVER_CONSOLE_MODE) {
}
}
export default globals;
const globalsProxy = new Proxy(globals, {
get: (originalTarget, property) => {
// if running storybook, refer to the StorybookGlobals when the property matches a key of that object
// this allows us to manipulate certain properties without affecting the console
const target =
!!process.env.STORYBOOK && property in storybookGlobals
? storybookGlobals
: originalTarget;
return Reflect.get(target, property);
},
});
const exportedGlobals = process.env.STORYBOOK ? globalsProxy : globals;
export default exportedGlobals;

View File

@ -4,6 +4,7 @@ import { ReactQueryDecorator } from '../../../../storybook/decorators/react-quer
import { eeLicenseInfo } from '../../../../features/EETrial/mocks/http';
import { About } from './About';
import { ConsoleTypeDecorator } from '../../../../storybook/decorators';
export default {
title: 'components/Services/Settings/About',
@ -38,10 +39,9 @@ export const WithoutEnterpriseLicense: StoryObj<typeof About> = {
<About serverVersion="v2.17.0" consoleAssetVersion="9acd324" />
</div>
),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [eeLicenseInfo.none],
consoleType: 'pro-lite',
},
};
@ -51,10 +51,9 @@ export const DeactivatedEnterpriseLicense: StoryObj<typeof About> = {
<About serverVersion="v2.17.0" consoleAssetVersion="9acd324" />
</div>
),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [eeLicenseInfo.deactivated],
consoleType: 'pro-lite',
},
};
@ -64,10 +63,9 @@ export const ExpiredEnterpriseLicense: StoryObj<typeof About> = {
<About serverVersion="v2.17.0" consoleAssetVersion="9acd324" />
</div>
),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [eeLicenseInfo.expired],
consoleType: 'pro-lite',
},
};
@ -77,9 +75,8 @@ export const ActiveEnterpriseLicense: StoryObj<typeof About> = {
<About serverVersion="v2.17.0" consoleAssetVersion="9acd324" />
</div>
),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};

View File

@ -7,6 +7,7 @@ import { QueryClient, QueryClientProvider } from 'react-query';
import { eeLicenseInfo } from '../../../features/EETrial/mocks/http';
import Sidebar, { Metadata } from './Sidebar';
import { HasuraMetadataV3 } from '../../../metadata/types';
import { ConsoleTypeDecorator } from '../../../storybook/decorators';
const queryClient = new QueryClient({
defaultOptions: {
@ -182,10 +183,9 @@ export const LogoutActive: StoryObj<typeof Sidebar> = {
name: '💠 Demo Pro Logout Active',
args: generateArgs(),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro', adminSecret: true })],
parameters: {
msw: [...mockHandlers({}), eeLicenseInfo.active],
adminSecretSet: true,
},
};
@ -196,10 +196,9 @@ export const ProLiteLoading: StoryObj<typeof Sidebar> = {
name: '💠 Demo Pro Lite Prometheus Loading',
args: generateArgs(),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: mockHandlers({ delay: 'infinite' }),
consoleType: 'pro-lite',
},
};
@ -210,10 +209,9 @@ export const ProLitePrometheusWithoutLicense: StoryObj<typeof Sidebar> = {
name: '💠 Demo Pro Lite Prometheus Without License',
args: generateArgs(),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [...mockHandlers({ prometheusEnabled: true }), eeLicenseInfo.none],
consoleType: 'pro-lite',
},
};
@ -225,9 +223,9 @@ export const ProLitePrometheusEnabled: StoryObj<typeof Sidebar> = {
name: '💠 Demo Pro Lite Prometheus Enabled',
args: generateArgs(),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [...mockHandlers({ prometheusEnabled: true }), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -238,10 +236,9 @@ export const ProLitePrometheusDisabled: StoryObj<typeof Sidebar> = {
name: '💠 Demo Pro Lite Prometheus Disabled',
args: generateArgs(),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [...mockHandlers({ prometheusEnabled: false }), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -252,10 +249,9 @@ export const ProLiteError: StoryObj<typeof Sidebar> = {
name: '💠 Demo Pro Lite Prometheus Error',
args: generateArgs(),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [...mockHandlers({ status: 500 }), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -266,10 +262,9 @@ export const ProLiteOpenTelemetryWithoutLicense: StoryObj<typeof Sidebar> = {
name: '💠 Demo Pro Lite OpenTelemetry Without License',
args: generateArgs(),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [...mockHandlers({ openTelemetryEnabled: false }), eeLicenseInfo.none],
consoleType: 'pro-lite',
},
};
@ -280,13 +275,12 @@ export const ProLiteOpenTelemetryEnabled: StoryObj<typeof Sidebar> = {
name: '💠 Demo Pro Lite OpenTelemetry Enabled',
args: generateArgs(),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [
...mockHandlers({ openTelemetryEnabled: true }),
eeLicenseInfo.active,
],
consoleType: 'pro-lite',
},
};
@ -297,12 +291,11 @@ export const ProLiteOpenTelemetryDisabled: StoryObj<typeof Sidebar> = {
name: '💠 Demo Pro Lite OpenTelemetry Disabled',
args: generateArgs(),
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: [
...mockHandlers({ openTelemetryEnabled: false }),
eeLicenseInfo.active,
],
consoleType: 'pro-lite',
},
};

View File

@ -5,6 +5,7 @@ import { isCloudConsole } from '../../utils';
import { ConnectDatabaseV2 } from './ConnectDatabase';
import { useEnvironmentState } from './hooks';
import { handlers } from './mocks/handlers.mock';
import { ConsoleTypeDecorator } from '../../storybook/decorators';
export default {
component: ConnectDatabaseV2,
@ -23,6 +24,7 @@ const Template: StoryObj<typeof ConnectDatabaseV2> = {
};
export const FromEnvironment: StoryObj<typeof ConnectDatabaseV2> = {
decorators: [ConsoleTypeDecorator({ consoleType: 'cloud-pro' })],
render: () => {
const env = useEnvironmentState();
const cloud = isCloudConsole(globals);
@ -32,7 +34,7 @@ export const FromEnvironment: StoryObj<typeof ConnectDatabaseV2> = {
This component attempts to read Console Type, and EE License Info from
the environment
</div>
<div>isCloud: {cloud.toString()}</div>
<div>is Cloud Console: {cloud.toString()}</div>
<div>Console Type: {globals.consoleType}</div>
<div>Tenant Id: {globals.hasuraCloudTenantId}</div>
<ConnectDatabaseV2 {...env} />

View File

@ -1,6 +1,7 @@
import { expect } from '@storybook/jest';
import { Meta, StoryObj } from '@storybook/react';
import { screen, userEvent, waitFor, within } from '@storybook/testing-library';
import { ConsoleTypeDecorator } from '../../../../storybook/decorators';
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
import { dismissToast } from '../../../../utils/StoryUtils';
import { NativeQuery } from '../../../hasura-metadata-types';
@ -101,9 +102,9 @@ export const WithRouteWrapper: Story = {
</RouteWrapper>
),
name: '🚏 Route Wrapper',
parameters: {
consoleType: 'pro',
},
decorators: [
ConsoleTypeDecorator({ consoleType: 'pro', menuPlacement: 'top' }),
],
};
export const HappyPath: Story = {

View File

@ -7,6 +7,7 @@ import { confirmAlert, dismissToast } from '../../../../utils/StoryUtils';
import { nativeQueryHandlers } from '../AddNativeQuery/mocks';
import { Routes } from '../constants';
import { LandingPage } from './LandingPage';
import { ConsoleTypeDecorator } from '../../../../storybook/decorators';
export default {
component: LandingPage,
@ -21,9 +22,8 @@ export const Basic: StoryObj<typeof LandingPage> = {
render: args => {
return <LandingPage pathname={Routes.NativeQueries} />;
},
decorators: [ConsoleTypeDecorator({ consoleType: 'pro' })],
parameters: {
consoleType: 'pro',
msw: nativeQueryHandlers({
metadataOptions: {
postgres: { models: true, queries: true },
@ -39,9 +39,8 @@ export const NoQueries: StoryObj<typeof LandingPage> = {
render: args => {
return <LandingPage pathname={Routes.NativeQueries} />;
},
decorators: [ConsoleTypeDecorator({ consoleType: 'pro' })],
parameters: {
consoleType: 'pro',
msw: nativeQueryHandlers({
metadataOptions: { postgres: { models: true, queries: false } },
}),
@ -52,9 +51,8 @@ export const NoModels: StoryObj<typeof LandingPage> = {
render: args => {
return <LandingPage pathname={Routes.NativeQueries} />;
},
decorators: [ConsoleTypeDecorator({ consoleType: 'pro' })],
parameters: {
consoleType: 'pro',
msw: nativeQueryHandlers({
metadataOptions: { postgres: { models: false, queries: true } },
}),
@ -140,9 +138,8 @@ export const HappyPath: StoryObj<typeof LandingPage> = {
},
name: '😊 Happy Path',
decorators: [ConsoleTypeDecorator({ consoleType: 'pro' })],
parameters: {
consoleType: 'pro',
msw: nativeQueryHandlers({
metadataOptions: {
postgres: { models: true, queries: true },
@ -164,9 +161,7 @@ export const Referenced: StoryObj<typeof LandingPage> = {
render: args => {
return <LandingPage pathname={Routes.NativeQueries} />;
},
name: '🚨 Cannot Remove Referenced Logical Model',
parameters: {
consoleType: 'pro',
msw: nativeQueryHandlers({
@ -205,9 +200,8 @@ export const NotFound: StoryObj<typeof LandingPage> = {
},
name: '🚨 Not found',
decorators: [ConsoleTypeDecorator({ consoleType: 'pro' })],
parameters: {
consoleType: 'pro',
msw: nativeQueryHandlers({
metadataOptions: {
postgres: { models: true, queries: true },
@ -239,9 +233,8 @@ export const Disabled: StoryObj<typeof LandingPage> = {
},
name: '🚨 Native Queries Disabled',
decorators: [ConsoleTypeDecorator({ consoleType: 'pro' })],
parameters: {
consoleType: 'pro',
msw: nativeQueryHandlers({
metadataOptions: {
postgres: { models: true, queries: true },
@ -269,7 +262,7 @@ export const Oss: StoryObj<typeof LandingPage> = {
},
name: '🚨 Native Queries Oss',
decorators: [ConsoleTypeDecorator({ consoleType: 'oss' })],
parameters: {
msw: nativeQueryHandlers({
metadataOptions: {
@ -279,7 +272,6 @@ export const Oss: StoryObj<typeof LandingPage> = {
untrackNativeQueryResult: 'native_queries_disabled',
untrackLogicalModelResult: 'native_queries_disabled',
}),
consoleType: 'oss',
},
};
@ -289,7 +281,7 @@ export const Pro: StoryObj<typeof LandingPage> = {
},
name: '🚨 Native Queries Pro',
decorators: [ConsoleTypeDecorator({ consoleType: 'pro' })],
parameters: {
msw: nativeQueryHandlers({
metadataOptions: {
@ -299,7 +291,6 @@ export const Pro: StoryObj<typeof LandingPage> = {
untrackNativeQueryResult: 'native_queries_disabled',
untrackLogicalModelResult: 'native_queries_disabled',
}),
consoleType: 'pro',
},
};
@ -309,7 +300,7 @@ export const ProLite: StoryObj<typeof LandingPage> = {
},
name: '🚨 Native Queries ProLite',
decorators: [ConsoleTypeDecorator({ consoleType: 'pro-lite' })],
parameters: {
msw: nativeQueryHandlers({
metadataOptions: {
@ -319,7 +310,6 @@ export const ProLite: StoryObj<typeof LandingPage> = {
untrackNativeQueryResult: 'native_queries_disabled',
untrackLogicalModelResult: 'native_queries_disabled',
}),
consoleType: 'pro-lite',
},
};
@ -329,7 +319,7 @@ export const FeatureFlagDisabled: StoryObj<typeof LandingPage> = {
},
name: '🚨 Native Queries FeatureFlagDisabled',
decorators: [ConsoleTypeDecorator({ consoleType: 'pro' })],
parameters: {
msw: nativeQueryHandlers({
metadataOptions: {
@ -340,6 +330,5 @@ export const FeatureFlagDisabled: StoryObj<typeof LandingPage> = {
untrackLogicalModelResult: 'native_queries_disabled',
enabledFeatureFlag: false,
}),
consoleType: 'pro',
},
};

View File

@ -7,23 +7,27 @@ import {
waitFor,
within,
} from '@storybook/testing-library';
import { ConsoleTypeDecorator } from '../../../../storybook/decorators';
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
import { dismissToast } from '../../../../utils/StoryUtils';
import { LogicalModel } from '../../../hasura-metadata-types';
import { waitForSpinnerOverlay } from '../../components/ReactQueryWrappers/story-utils';
import { logicalModelFieldToFormField } from '../LogicalModel/utils/logicalModelFieldToFormField';
import { LogicalModelWidget } from './LogicalModelWidget';
import { handlers } from './mocks/handlers';
import { defaultEmptyValues } from './validationSchema';
import {
LOGICAL_MODEL_CREATE_ERROR,
LOGICAL_MODEL_CREATE_SUCCESS,
LOGICAL_MODEL_EDIT_SUCCESS,
} from '../constants';
import { LogicalModelWidget } from './LogicalModelWidget';
import { handlers } from './mocks/handlers';
import { defaultEmptyValues } from './validationSchema';
export default {
component: LogicalModelWidget,
decorators: [ReactQueryDecorator()],
decorators: [
ConsoleTypeDecorator({ consoleType: 'pro' }),
ReactQueryDecorator(),
],
} as Meta<typeof LogicalModelWidget>;
export const DefaultView: StoryObj<typeof LogicalModelWidget> = {

View File

@ -1,31 +1,17 @@
import React from 'react';
import { StoryObj, Meta } from '@storybook/react';
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
import {
registerEETrialLicenseActiveMutation,
registerEETrialLicenseDeactivatedMutation,
registerEETrialLicenseExpiredMutation,
} from '../../mocks/registration.mock';
import { ApiSecurityTabEELiteWrapper } from './ApiSecurityTab';
import { Meta, StoryObj } from '@storybook/react';
import { SecurityTabs } from '../../../../components/Services/ApiExplorer/Security/SecurityTabs';
import { ConsoleTypeDecorator } from '../../../../storybook/decorators';
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
import { eeLicenseInfo } from '../../mocks/http';
import { useQueryClient } from 'react-query';
import { EE_LICENSE_INFO_QUERY_NAME } from '../../constants';
import { registerEETrialLicenseActiveMutation } from '../../mocks/registration.mock';
import { ApiSecurityTabEELiteWrapper } from './ApiSecurityTab';
export default {
title: 'features / EETrial / API Security Tab 🧬️',
component: ApiSecurityTabEELiteWrapper,
decorators: [
// This is done so as we have set some cache time on the EE_LICENSE_INFO_QUERY_NAME query.
// So we need to refetch the cache data, so it doesn't persist across different stories. And
// it makes sure that our component actually does the network call, letting msw mocks return the
// desired response.
Story => {
const queryClient = useQueryClient();
void queryClient.refetchQueries(EE_LICENSE_INFO_QUERY_NAME);
return <Story />;
},
ReactQueryDecorator(),
ConsoleTypeDecorator({ consoleType: 'pro-lite' }),
],
} as Meta<typeof ApiSecurityTabEELiteWrapper>;
@ -42,7 +28,6 @@ export const Default: StoryObj<typeof ApiSecurityTabEELiteWrapper> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.none],
consoleType: 'pro-lite',
},
};
@ -59,7 +44,6 @@ export const LicenseActive: StoryObj<typeof ApiSecurityTabEELiteWrapper> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -75,8 +59,7 @@ export const LicenseExpired: StoryObj<typeof ApiSecurityTabEELiteWrapper> = {
name: '💠 License Expired',
parameters: {
msw: [registerEETrialLicenseExpiredMutation, eeLicenseInfo.expired],
consoleType: 'pro-lite',
msw: [eeLicenseInfo.expired],
},
};
@ -93,10 +76,6 @@ export const LicenseDeactivated: StoryObj<typeof ApiSecurityTabEELiteWrapper> =
name: '💠 License Deactivated',
parameters: {
msw: [
registerEETrialLicenseDeactivatedMutation,
eeLicenseInfo.deactivated,
],
consoleType: 'pro-lite',
msw: [eeLicenseInfo.deactivated],
},
};

View File

@ -1,31 +1,17 @@
import React from 'react';
import { StoryObj, Meta } from '@storybook/react';
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
import { Meta, StoryObj } from '@storybook/react';
import { AutoCleanupForm } from '../../../../components/Services/Events/EventTriggers/Common/AutoCleanupForm';
import {
registerEETrialLicenseActiveMutation,
registerEETrialLicenseDeactivatedMutation,
registerEETrialLicenseExpiredMutation,
} from '../../mocks/registration.mock';
import { ETAutoCleanupWrapper } from './ETAutoCleanupWrapper';
import { ConsoleTypeDecorator } from '../../../../storybook/decorators';
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
import { eeLicenseInfo } from '../../mocks/http';
import { EE_LICENSE_INFO_QUERY_NAME } from '../../constants';
import { useQueryClient } from 'react-query';
import { registerEETrialLicenseActiveMutation } from '../../mocks/registration.mock';
import { ETAutoCleanupWrapper } from './ETAutoCleanupWrapper';
export default {
title: 'features / EETrial / Event Trigger Auto Cleanup Card 🧬️',
component: ETAutoCleanupWrapper,
decorators: [
// This is done so as we have set some cache time on the EE_LICENSE_INFO_QUERY_NAME query.
// So we need to refetch the cache data, so it doesn't persist across different stories. And
// it makes sure that our component actually does the network call, letting msw mocks return the
// desired response.
Story => {
const queryClient = useQueryClient();
void queryClient.refetchQueries(EE_LICENSE_INFO_QUERY_NAME);
return <Story />;
},
ReactQueryDecorator(),
ConsoleTypeDecorator({ consoleType: 'pro-lite' }),
],
} as Meta<typeof ETAutoCleanupWrapper>;
@ -42,7 +28,6 @@ export const Default: StoryObj<typeof ETAutoCleanupWrapper> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.none],
consoleType: 'pro-lite',
},
};
@ -59,7 +44,6 @@ export const LicenseActive: StoryObj<typeof ETAutoCleanupWrapper> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -75,8 +59,7 @@ export const LicenseExpired: StoryObj<typeof ETAutoCleanupWrapper> = {
name: '💠 License Expired',
parameters: {
msw: [registerEETrialLicenseExpiredMutation, eeLicenseInfo.expired],
consoleType: 'pro-lite',
msw: [eeLicenseInfo.expired],
},
};
@ -92,7 +75,6 @@ export const LicenseDeactivated: StoryObj<typeof ETAutoCleanupWrapper> = {
name: '💠 License Deactivated',
parameters: {
msw: [registerEETrialLicenseDeactivatedMutation, eeLicenseInfo.deactivated],
consoleType: 'pro-lite',
msw: [eeLicenseInfo.deactivated],
},
};

View File

@ -1,26 +1,16 @@
import React from 'react';
import { StoryObj, Meta } from '@storybook/react';
import { Meta, StoryObj } from '@storybook/react';
import { ConsoleTypeDecorator } from '../../../../storybook/decorators';
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
import { eeLicenseInfo } from '../../mocks/http';
import { registerEETrialLicenseActiveMutation } from '../../mocks/registration.mock';
import { MultipleAdminSecretsPage } from './MultipleAdminSecretsPage';
import { eeLicenseInfo } from '../../mocks/http';
import { useQueryClient } from 'react-query';
import { EE_LICENSE_INFO_QUERY_NAME } from '../../constants';
export default {
title: 'features / EETrial / Multiple Admin Secrets Page 🧬️',
component: MultipleAdminSecretsPage,
decorators: [
// This is done so as we have set some cache time on the EE_LICENSE_INFO_QUERY_NAME query.
// So we need to refetch the cache data, so it doesn't persist across different stories. And
// it makes sure that our component actually does the network call, letting msw mocks return the
// desired response.
Story => {
const queryClient = useQueryClient();
void queryClient.refetchQueries(EE_LICENSE_INFO_QUERY_NAME);
return <Story />;
},
ReactQueryDecorator(),
ConsoleTypeDecorator({ consoleType: 'pro-lite' }),
],
} as Meta<typeof MultipleAdminSecretsPage>;
@ -33,7 +23,6 @@ export const Default: StoryObj<typeof MultipleAdminSecretsPage> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.none],
consoleType: 'pro-lite',
},
};
@ -46,7 +35,6 @@ export const LicenseActive: StoryObj<typeof MultipleAdminSecretsPage> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -59,7 +47,6 @@ export const LicenseExpired: StoryObj<typeof MultipleAdminSecretsPage> = {
parameters: {
msw: [eeLicenseInfo.expired],
consoleType: 'pro-lite',
},
};
@ -72,6 +59,5 @@ export const LicenseDeactivated: StoryObj<typeof MultipleAdminSecretsPage> = {
parameters: {
msw: [eeLicenseInfo.deactivated],
consoleType: 'pro-lite',
},
};

View File

@ -1,30 +1,16 @@
import React from 'react';
import { StoryObj, Meta } from '@storybook/react';
import { Meta, StoryObj } from '@storybook/react';
import { ConsoleTypeDecorator } from '../../../../storybook/decorators';
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
import {
registerEETrialLicenseActiveMutation,
registerEETrialLicenseDeactivatedMutation,
registerEETrialLicenseExpiredMutation,
} from '../../mocks/registration.mock';
import { MultipleJWTSecretsPage } from './MultipleJWTSecretsPage';
import { eeLicenseInfo } from '../../mocks/http';
import { useQueryClient } from 'react-query';
import { EE_LICENSE_INFO_QUERY_NAME } from '../../constants';
import { registerEETrialLicenseActiveMutation } from '../../mocks/registration.mock';
import { MultipleJWTSecretsPage } from './MultipleJWTSecretsPage';
export default {
title: 'features / EETrial / Multiple JWT Secrets Page 🧬️',
component: MultipleJWTSecretsPage,
decorators: [
// This is done so as we have set some cache time on the EE_LICENSE_INFO_QUERY_NAME query.
// So we need to refetch the cache data, so it doesn't persist across different stories. And
// it makes sure that our component actually does the network call, letting msw mocks return the
// desired response.
Story => {
const queryClient = useQueryClient();
void queryClient.refetchQueries(EE_LICENSE_INFO_QUERY_NAME);
return <Story />;
},
ReactQueryDecorator(),
ConsoleTypeDecorator({ consoleType: 'pro-lite' }),
],
} as Meta<typeof MultipleJWTSecretsPage>;
@ -37,7 +23,6 @@ export const Default: StoryObj<typeof MultipleJWTSecretsPage> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.none],
consoleType: 'pro-lite',
},
};
@ -50,7 +35,6 @@ export const LicenseActive: StoryObj<typeof MultipleJWTSecretsPage> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -62,8 +46,7 @@ export const LicenseExpired: StoryObj<typeof MultipleJWTSecretsPage> = {
name: '💠 License Expired',
parameters: {
msw: [registerEETrialLicenseExpiredMutation, eeLicenseInfo.expired],
consoleType: 'pro-lite',
msw: [eeLicenseInfo.expired],
},
};
@ -75,7 +58,6 @@ export const LicenseDeactivated: StoryObj<typeof MultipleJWTSecretsPage> = {
name: '💠 License Deactivated',
parameters: {
msw: [registerEETrialLicenseDeactivatedMutation, eeLicenseInfo.deactivated],
consoleType: 'pro-lite',
msw: [eeLicenseInfo.deactivated],
},
};

View File

@ -1,64 +1,48 @@
import React from 'react';
import { StoryObj, Meta } from '@storybook/react';
import { Meta, StoryObj } from '@storybook/react';
import { ReactQueryDecorator } from '../../../storybook/decorators/react-query';
import { eeLicenseInfo } from '../mocks/http';
import { ConsoleTypeDecorator } from '../../../storybook/decorators';
import { NavbarButton as EnterpriseButton } from './NavbarButton';
import { useQueryClient } from 'react-query';
import { EE_LICENSE_INFO_QUERY_NAME } from '../constants';
export default {
title: 'features/EETrial/NavbarButton',
component: EnterpriseButton,
decorators: [
// This is done so as we have set some cache time on the EE_LICENSE_INFO_QUERY_NAME query.
// So we need to refetch the cache data, so it doesn't persist across different stories. And
// it makes sure that our component actually does the network call, letting msw mocks return the
// desired response.
Story => {
const queryClient = useQueryClient();
void queryClient.refetchQueries(EE_LICENSE_INFO_QUERY_NAME);
return <Story />;
},
ReactQueryDecorator(),
ConsoleTypeDecorator({ consoleType: 'pro-lite' }),
],
} as Meta<typeof EnterpriseButton>;
export const Loading: StoryObj<typeof EnterpriseButton> = {
render: args => (
<div className="w-full h-20 bg-slate-700 flex justify-center items-center">
<EnterpriseButton globals={{ consoleType: 'pro-lite' } as any} />
<EnterpriseButton />
</div>
),
parameters: {
consoleType: 'pro-lite',
},
};
export const NoEnterpriseLicense: StoryObj<typeof EnterpriseButton> = {
render: args => (
<div className="w-full h-20 bg-slate-700 flex justify-center items-center">
<EnterpriseButton globals={{ consoleType: 'pro-lite' } as any} />
<EnterpriseButton />
</div>
),
parameters: {
msw: [eeLicenseInfo.none],
consoleType: 'pro-lite',
},
};
export const ActiveEnterpriceLicense: StoryObj<typeof EnterpriseButton> = {
render: args => (
<div className="w-full h-20 bg-slate-700 flex justify-center items-center">
<EnterpriseButton globals={{ consoleType: 'pro-lite' } as any} />
<EnterpriseButton />
</div>
),
parameters: {
msw: [eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -67,13 +51,12 @@ export const ExpiredEnterpriseLicenseWithGrace: StoryObj<
> = {
render: args => (
<div className="w-full h-20 bg-slate-700 flex justify-center items-center">
<EnterpriseButton globals={{ consoleType: 'pro-lite' } as any} />
<EnterpriseButton />
</div>
),
parameters: {
msw: [eeLicenseInfo.expiredWithoutGrace],
consoleType: 'pro-lite',
},
};
@ -82,7 +65,7 @@ export const ExpiredEnterpriseLicenseWithoutGrace: StoryObj<
> = {
render: args => (
<div className="w-full h-20 bg-slate-700 flex justify-center items-center">
<EnterpriseButton globals={{ consoleType: 'pro-lite' } as any} />
<EnterpriseButton />
</div>
),
};
@ -92,25 +75,23 @@ export const ExpiredEnterpriseLicenseAfterGrace: StoryObj<
> = {
render: args => (
<div className="w-full h-20 bg-slate-700 flex justify-center items-center">
<EnterpriseButton globals={{ consoleType: 'pro-lite' } as any} />
<EnterpriseButton />
</div>
),
parameters: {
msw: [eeLicenseInfo.expiredAfterGrace],
consoleType: 'pro-lite',
},
};
export const DeactivatedEnterpriseLicense: StoryObj<typeof EnterpriseButton> = {
render: args => (
<div className="w-full h-20 bg-slate-700 flex justify-center items-center">
<EnterpriseButton globals={{ consoleType: 'pro-lite' } as any} />
<EnterpriseButton />
</div>
),
parameters: {
msw: [eeLicenseInfo.deactivated],
consoleType: 'pro-lite',
},
};

View File

@ -1,26 +1,16 @@
import React from 'react';
import { StoryObj, Meta } from '@storybook/react';
import { Meta, StoryObj } from '@storybook/react';
import { ConsoleTypeDecorator } from '../../../../storybook/decorators';
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
import { eeLicenseInfo } from '../../mocks/http';
import { registerEETrialLicenseActiveMutation } from '../../mocks/registration.mock';
import { SingleSignOnPage } from './SingleSignOnPage';
import { eeLicenseInfo } from '../../mocks/http';
import { useQueryClient } from 'react-query';
import { EE_LICENSE_INFO_QUERY_NAME } from '../../constants';
export default {
title: 'features / EETrial / Single Sign On (SSO) Page 🧬️',
component: SingleSignOnPage,
decorators: [
// This is done so as we have set some cache time on the EE_LICENSE_INFO_QUERY_NAME query.
// So we need to refetch the cache data, so it doesn't persist across different stories. And
// it makes sure that our component actually does the network call, letting msw mocks return the
// desired response.
Story => {
const queryClient = useQueryClient();
void queryClient.refetchQueries(EE_LICENSE_INFO_QUERY_NAME);
return <Story />;
},
ReactQueryDecorator(),
ConsoleTypeDecorator({ consoleType: 'pro-lite' }),
],
} as Meta<typeof SingleSignOnPage>;
@ -33,7 +23,6 @@ export const Default: StoryObj<typeof SingleSignOnPage> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.none],
consoleType: 'pro-lite',
},
};
@ -46,7 +35,6 @@ export const LicenseActive: StoryObj<typeof SingleSignOnPage> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -59,7 +47,6 @@ export const LicenseExpired: StoryObj<typeof SingleSignOnPage> = {
parameters: {
msw: [eeLicenseInfo.expired],
consoleType: 'pro-lite',
},
};
@ -72,6 +59,5 @@ export const LicenseDeactivated: StoryObj<typeof SingleSignOnPage> = {
parameters: {
msw: [eeLicenseInfo.deactivated],
consoleType: 'pro-lite',
},
};

View File

@ -8,6 +8,7 @@ import { OpenTelemetryFeature } from './OpenTelemetryFeature';
import { eeLicenseInfo } from '../EETrial/mocks/http';
import { registerEETrialLicenseActiveMutation } from '../EETrial/mocks/registration.mock';
import { HasuraMetadataV3 } from '../../metadata/types';
import { ConsoleTypeDecorator } from '../../storybook/decorators';
const queryClient = new QueryClient({
defaultOptions: {
@ -92,6 +93,7 @@ export default {
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
),
ConsoleTypeDecorator({ consoleType: 'pro-lite' }),
],
} as Meta<typeof OpenTelemetryFeature>;
@ -109,7 +111,6 @@ export const DisabledWithoutLicense: StoryObj<typeof OpenTelemetryFeature> = {
registerEETrialLicenseActiveMutation,
eeLicenseInfo.active,
],
consoleType: 'pro-lite',
},
};
@ -122,7 +123,6 @@ export const Loading: StoryObj<typeof OpenTelemetryFeature> = {
parameters: {
msw: [mockMetadataHandler(true, 'infinite'), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -135,7 +135,6 @@ export const Enabled: StoryObj<typeof OpenTelemetryFeature> = {
parameters: {
msw: [mockMetadataHandler(true, 1), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -148,7 +147,6 @@ export const Disabled: StoryObj<typeof OpenTelemetryFeature> = {
parameters: {
msw: [mockMetadataHandler(false, 1), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -161,6 +159,5 @@ export const Error: StoryObj<typeof OpenTelemetryFeature> = {
parameters: {
msw: [mockMetadataHandler(false, 1, 500), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};

View File

@ -9,6 +9,7 @@ import {
handlers,
} from '../../../mocks/metadata.mock';
import { ConsoleTypeDecorator } from '../../../storybook/decorators';
import { OpenTelemetryEEProvider } from './OpenTelemetryEEProvider';
// --------------------------------------------------
@ -23,6 +24,7 @@ export default {
decorators: [
ReduxDecorator({ tables: { currentDataSource: 'default' } }),
ReactQueryDecorator(),
ConsoleTypeDecorator({ consoleType: 'pro' }),
],
parameters: {
msw: handlers({
@ -52,7 +54,6 @@ export const HappyPath: StoryObj<typeof OpenTelemetryEEProvider> = {
parameters: {
chromatic: { disableSnapshot: true },
consoleType: 'pro',
msw: handlers({
// Speeds up the test as much as possible
delay: 0,

View File

@ -1,8 +1,10 @@
import { Meta, StoryObj } from '@storybook/react';
import { LogicalModelPermissionsPage } from './LogicalModelPermissionsPage';
import { handlers, deleteHandlers } from './mocks';
import { RouteWrapper } from '../../Data/LogicalModels/components/RouteWrapper';
import { ReactQueryDecorator } from '../../../storybook/decorators/react-query';
import { ConsoleTypeDecorator } from '../../../storybook/decorators';
const name = 'LogicalModel';
const source = 'Postgres';
@ -21,7 +23,10 @@ export default {
</RouteWrapper>
);
},
decorators: [ReactQueryDecorator()],
decorators: [
ReactQueryDecorator(),
ConsoleTypeDecorator({ consoleType: 'pro' }),
],
} as Meta;
type Story = StoryObj<typeof LogicalModelPermissionsPage>;
@ -60,7 +65,6 @@ export const SavePermission: Story = {
// },
parameters: {
msw: handlers(),
consoleType: 'pro',
},
};
@ -100,6 +104,5 @@ export const DeletePermission: Story = {
// },
parameters: {
msw: deleteHandlers(),
consoleType: 'pro',
},
};

View File

@ -7,6 +7,7 @@ import { ReactQueryDevtools } from 'react-query/devtools';
import { PrometheusSettings } from '.';
import { eeLicenseInfo } from '../EETrial/mocks/http';
import { registerEETrialLicenseActiveMutation } from '../EETrial/mocks/registration.mock';
import { ConsoleTypeDecorator } from '../../storybook/decorators';
const queryClient = new QueryClient({
defaultOptions: {
@ -72,6 +73,7 @@ export default {
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
),
ConsoleTypeDecorator({ consoleType: 'pro-lite' }),
],
} as Meta<typeof PrometheusSettings>;
@ -86,7 +88,6 @@ export const DisabledWithoutLicense: StoryObj<typeof PrometheusSettings> = {
registerEETrialLicenseActiveMutation,
eeLicenseInfo.active,
],
consoleType: 'pro-lite',
},
};
@ -97,7 +98,6 @@ export const Loading: StoryObj<typeof PrometheusSettings> = {
parameters: {
msw: [mockConfigHandler(true, 'infinite'), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -108,7 +108,6 @@ export const Enabled: StoryObj<typeof PrometheusSettings> = {
parameters: {
msw: [mockConfigHandler(true, 1), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -119,7 +118,6 @@ export const Disabled: StoryObj<typeof PrometheusSettings> = {
parameters: {
msw: [mockConfigHandler(false, 1), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};
@ -130,6 +128,5 @@ export const Error: StoryObj<typeof PrometheusSettings> = {
parameters: {
msw: [mockConfigHandler(false, 1, 500), eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};

View File

@ -1,15 +1,13 @@
import React from 'react';
import { StoryObj, Meta } from '@storybook/react';
import { Meta, StoryObj } from '@storybook/react';
import { QueryResponseCaching } from './QueryResponseCaching';
import { ConsoleTypeDecorator } from '../../storybook/decorators';
import { ReactQueryDecorator } from '../../storybook/decorators/react-query';
import { eeLicenseInfo } from '../EETrial/mocks/http';
import {
registerEETrialLicenseActiveMutation,
registerEETrialLicenseAlreadyAppliedMutation,
} from '../EETrial/mocks/registration.mock';
import { eeLicenseInfo } from '../EETrial/mocks/http';
import { useQueryClient } from 'react-query';
import { ReactQueryDecorator } from '../../storybook/decorators/react-query';
import { EE_LICENSE_INFO_QUERY_NAME } from '../EETrial';
import { QueryResponseCaching } from './QueryResponseCaching';
export default {
title: 'Features/Settings/Query Response Caching',
@ -20,16 +18,8 @@ export default {
},
},
decorators: [
// This is done so as we have set some cache time on the EE_LICENSE_INFO_QUERY_NAME query.
// So we need to refetch the cache data, so it doesn't persist across different stories. And
// it makes sure that our component actually does the network call, letting msw mocks return the
// desired response.
Story => {
const queryClient = useQueryClient();
void queryClient.refetchQueries(EE_LICENSE_INFO_QUERY_NAME);
return <Story />;
},
ReactQueryDecorator(),
ConsoleTypeDecorator({ consoleType: 'pro-lite' }),
],
} as Meta<typeof QueryResponseCaching>;
@ -42,7 +32,6 @@ export const UnregisteredUser: StoryObj<typeof QueryResponseCaching> = {
parameters: {
msw: [registerEETrialLicenseActiveMutation, eeLicenseInfo.none],
consoleType: 'pro-lite',
},
};
@ -55,6 +44,5 @@ export const LicenseActive: StoryObj<typeof QueryResponseCaching> = {
parameters: {
msw: [registerEETrialLicenseAlreadyAppliedMutation, eeLicenseInfo.active],
consoleType: 'pro-lite',
},
};

View File

@ -0,0 +1,85 @@
import Select from 'react-select';
import { Button } from '../../../new-components/Button';
import { Switch } from '../../../new-components/Switch';
import hasuraIcon from '../images/hasura-icon-mono-light.svg';
import { consoleTypeDropDownArray } from './constants';
import { useMenuContentStyles } from './menu-container-styles';
import { ConsoleTypes, EnvStateArgs } from './types';
export type MenuPlacement = 'top' | 'bottom';
type MenuContentProps = {
menuPlacement: MenuPlacement;
minimized: boolean;
handleTriggerClick: () => void;
handleAdminSwitchChange: (enabled: boolean) => void;
handleConsoleTypeChange: (
option: { value: ConsoleTypes; label: string } | null
) => void;
handleMinimizeClick: () => void;
envArgsState: EnvStateArgs;
};
export const MenuContent = ({
menuPlacement,
minimized,
handleMinimizeClick,
handleTriggerClick,
handleAdminSwitchChange,
handleConsoleTypeChange,
envArgsState,
}: MenuContentProps) => {
const {
styles,
mainMenuContainerClass,
triggerButtonClass,
menuContainerClass,
} = useMenuContentStyles({ menuPlacement, minimized });
return (
<div data-chromatic="ignore" className={menuContainerClass}>
<div className={triggerButtonClass}>
<button
type="button"
onClick={handleTriggerClick}
className={styles.button}
>
<img
src={hasuraIcon}
style={{ height: 26 }}
alt="Console Type Dev Tools"
/>
</button>
</div>
<div className={mainMenuContainerClass}>
<Button
className="self-center"
size="sm"
onClick={() => handleMinimizeClick()}
>
Close
</Button>
<div className={styles.controlContainer}>
<div className={styles.label}>Admin Secret:</div>
<Switch
checked={envArgsState.adminSecret}
className="mt-2"
onCheckedChange={handleAdminSwitchChange}
/>
</div>
<div className={styles.controlContainer}>
<div className={styles.label}>Console Type:</div>
<Select
className="min-w-[100px]"
menuPlacement="top"
isSearchable={false}
options={consoleTypeDropDownArray}
value={consoleTypeDropDownArray.find(
t => t.value === envArgsState.consoleType
)}
onChange={handleConsoleTypeChange}
/>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,100 @@
import { Decorator } from '@storybook/react';
import React, { ReactNode, useCallback, useEffect, useState } from 'react';
import { MenuContent } from './MenuContent';
import { updateStorybookGlobals } from './storybook-globals';
import { ConsoleTypes, EnvStateArgs, MenuOptions } from './types';
import { createEnvState } from './utils';
export const ConsoleTypeDecorator = (
args: EnvStateArgs & MenuOptions
): Decorator => {
const { consoleType, adminSecret, ...menuOptions } = args;
return Story => (
<ConsoleTypeWrapper
args={{ consoleType, adminSecret }}
menuPlacement={menuOptions.menuPlacement ?? 'bottom'}
>
<Story />
</ConsoleTypeWrapper>
);
};
const ConsoleTypeWrapper = ({
args = { consoleType: 'oss', adminSecret: false },
children,
menuPlacement = 'bottom',
}: {
args: EnvStateArgs;
children: ReactNode;
menuPlacement?: 'top' | 'bottom';
}) => {
// a simple boolean to stall rendering children until we've updated the window.__env in the mount effect
const [render, setRender] = useState(false);
// this is used to apply a key to the div containing the children so it will force it to re-create if it changes
const [envArgsState, setEnvArgsState] = useState<EnvStateArgs>(args);
// this just applies a useable __env object to the window property
const updateState = useCallback(
(consoleType?: ConsoleTypes, adminSecret?: boolean) => {
const state = createEnvState({
adminSecret: adminSecret ?? args.adminSecret, // fallback to what was passed in via decorator
consoleType: consoleType ?? args.consoleType, // fallback to what was passed in via decorator
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// update the window for anything that relies on that directly
window.__env = { ...window.__env, ...state };
// update the storybook globals object that's used via Proxy in the primary globals.ts
updateStorybookGlobals({
...state,
isAdminSecretSet: !!state.adminSecret,
hasuraCloudTenantId: state.tenantID,
});
},
[args]
);
useEffect(() => {
// update the window on mount:
updateState();
// then, render the content
setRender(true);
}, []);
const [minimized, setMinimized] = useState(true);
return (
<>
<MenuContent
envArgsState={envArgsState}
menuPlacement={menuPlacement}
minimized={minimized}
handleTriggerClick={() => setMinimized(false)}
handleAdminSwitchChange={enabled => {
updateState(envArgsState.consoleType, enabled);
setEnvArgsState(prev => ({ ...prev, adminSecret: enabled }));
}}
handleConsoleTypeChange={option => {
if (option) {
updateState(option.value);
setEnvArgsState(prev => ({
...prev,
consoleType: option.value,
}));
}
}}
handleMinimizeClick={() => setMinimized(true)}
/>
{render && (
// use a key to tell the story to re-create the component if the console type/admin secret change
<React.Fragment key={JSON.stringify(envArgsState)}>
{children}
</React.Fragment>
)}
</>
);
};

View File

@ -0,0 +1,12 @@
import { ConsoleTypes } from './types';
export const consoleTypeDropDownArray: {
value: ConsoleTypes;
label: string;
}[] = [
{ value: 'oss', label: 'OSS' },
{ value: 'pro-lite', label: 'EE Lite' },
{ value: 'pro', label: 'EE' },
{ value: 'cloud', label: 'Cloud' },
{ value: 'cloud-pro', label: 'Cloud EE' },
];

View File

@ -0,0 +1,94 @@
import clsx from 'clsx';
import { MenuPlacement } from './MenuContent';
export const twStyles = {
label: `font-bold text-[#2E3438] text-[12px]`,
menuContainer: {
base: `right-0 mr-4 rounded-t-md z-[99999] fixed pointer-events-auto`,
position: {
top: 'top-0',
bottom: 'bottom-0',
},
},
triggerButton: {
base: `absolute right-0 -mr-1 transition-all`,
position: {
bottom: `bottom-0 mb-3`,
},
notMinimized: {
base: `opacity-0 h-0 invisible`,
translate: {
top: `translate-x-1/2`,
bottom: `-translate-x-1/2`,
},
},
},
mainMenuContainer: {
base: `border border-b-0 bg-white flex items-start h-24 p-4 justify-end gap-5`,
position: {
top: `rounded-t-none rounded-b-md !border-b border-t-0`,
},
transition: `transition-all`,
minimized: {
base: `opacity-0 h-0 invisible`,
translate: {
top: `-translate-y-1/2`,
bottom: `translate-y-1/2`,
},
},
},
controlContainer: `gap-1 flex flex-col`,
button: `active:shadow-[0_0_1px_1px_#1B2738] active:opacity-70 h-[46px] mt-4 w-[46px] bg-[#1B2738] shadow-[0_0_2px_2px_#1B2738] rounded-full`,
};
// Functions to generate classNames
export const getMenuContainerClass = (menuPlacement: MenuPlacement): string =>
clsx(
twStyles.menuContainer.base,
twStyles.menuContainer.position[menuPlacement]
);
export const getTriggerButtonClass = (
menuPlacement: MenuPlacement,
minimized: boolean
): string =>
clsx(
twStyles.triggerButton.base,
menuPlacement === 'bottom' && twStyles.triggerButton.position.bottom,
!minimized && twStyles.triggerButton.notMinimized.base,
!minimized && twStyles.triggerButton.notMinimized.translate[menuPlacement]
);
export const getMainMenuContainerClass = (
menuPlacement: MenuPlacement,
minimized: boolean
): string =>
clsx(
twStyles.mainMenuContainer.base,
menuPlacement === 'top' && twStyles.mainMenuContainer.position.top,
twStyles.mainMenuContainer.transition,
minimized && twStyles.mainMenuContainer.minimized.base,
minimized && twStyles.mainMenuContainer.minimized.translate[menuPlacement]
);
export function useMenuContentStyles({
menuPlacement,
minimized,
}: {
menuPlacement: MenuPlacement;
minimized: boolean;
}) {
const menuContainerClass = getMenuContainerClass(menuPlacement);
const triggerButtonClass = getTriggerButtonClass(menuPlacement, minimized);
const mainMenuContainerClass = getMainMenuContainerClass(
menuPlacement,
minimized
);
return {
styles: twStyles,
menuContainerClass,
triggerButtonClass,
mainMenuContainerClass,
};
}

View File

@ -0,0 +1,16 @@
import { StorybookGlobals } from './types';
// are used in place of globals via Proxy when running storybook
// the way it works is that any property that is declared here will be used in place of the regular globals object
// so we can build on this over time simply by adding properties
export let storybookGlobals: StorybookGlobals = {
consoleType: 'oss',
adminSecret: undefined,
isAdminSecretSet: false,
hasuraCloudTenantId: undefined,
};
// need an updater function as you can't mutate an import in other modules
export const updateStorybookGlobals = (updated: StorybookGlobals) => {
storybookGlobals = { ...updated };
};

View File

@ -0,0 +1,20 @@
export type ConsoleTypes = 'oss' | 'pro-lite' | 'pro' | 'cloud' | 'cloud-pro';
export type EnvState = Partial<typeof window.__env> & {
adminSecret: string | undefined;
consoleType: ConsoleTypes;
};
export type EnvStateArgs = {
consoleType: ConsoleTypes;
adminSecret?: boolean;
};
export type MenuOptions = { menuPlacement?: 'top' | 'bottom' };
export type StorybookGlobals = {
consoleType: ConsoleTypes;
isAdminSecretSet: boolean;
adminSecret: string | undefined;
hasuraCloudTenantId: string | undefined;
};

View File

@ -0,0 +1,14 @@
import { EnvStateArgs, EnvState } from './types';
// creates a partial of what goes on window.__env
export const createEnvState = ({
consoleType,
adminSecret,
}: EnvStateArgs): EnvState => {
return {
consoleType: consoleType === 'cloud-pro' ? 'cloud' : consoleType,
isAdminSecretSet: adminSecret,
adminSecret: adminSecret ? '*******' : undefined,
tenantID: consoleType === 'cloud-pro' ? 'tenant-id' : undefined, // globals.hasuraCloudTenantId
};
};

View File

@ -0,0 +1,11 @@
<svg width="81" height="84" viewBox="0 0 81 84" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5273_22558)">
<path d="M79.7186 28.6019C82.1218 21.0731 80.6778 6.03602 76.0158 0.487869C75.4073 -0.238057 74.2624 -0.134353 73.757 0.664165L68.0121 9.72787C66.5887 11.5427 64.0308 11.9575 62.1124 10.6923C55.8827 6.59602 48.4359 4.21083 40.4322 4.21083C32.4285 4.21083 24.9817 6.59602 18.752 10.6923C16.8336 11.9575 14.2757 11.5323 12.8523 9.72787L7.10738 0.664165C6.60199 -0.134353 5.45712 -0.238057 4.84859 0.487869C0.186621 6.03602 -1.25735 21.0731 1.14583 28.6019C1.94002 31.1012 2.16693 33.7456 1.69248 36.3279C1.22834 38.879 0.753897 41.9693 0.753897 44.1056C0.753897 66.1323 18.5251 84.0005 40.4322 84.0005C62.3497 84.0005 80.1105 66.1427 80.1105 44.1056C80.1105 41.959 79.6464 38.879 79.1719 36.3279C78.6975 33.7456 78.9244 31.1012 79.7186 28.6019ZM40.4322 75.0819C23.4965 75.0819 9.71684 61.2271 9.71684 44.199C9.71684 43.639 9.73747 43.0893 9.7581 42.5397C10.3769 30.9353 17.3802 21.0108 27.3024 16.2819C31.2836 14.3738 35.7393 13.316 40.4322 13.316C45.1251 13.316 49.5808 14.3842 53.5724 16.2923C63.4945 21.0212 70.4978 30.9456 71.1166 42.5397C71.1476 43.0893 71.1579 43.639 71.1579 44.199C71.1476 61.2271 57.3679 75.0819 40.4322 75.0819Z" fill="white"/>
<path d="M53.7366 56.083L45.8876 42.4045L39.1525 30.997C38.9978 30.7274 38.709 30.5615 38.3893 30.5615H31.9533C31.6335 30.5615 31.3448 30.7378 31.19 31.0074C31.0353 31.2874 31.0353 31.6296 31.2004 31.8993L37.6363 42.7882L28.9931 56.0415C28.8178 56.3111 28.7972 56.6637 28.9519 56.9541C29.1066 57.2445 29.4057 57.4208 29.7254 57.4208H36.2027C36.5018 57.4208 36.7803 57.2652 36.9453 57.0163L41.6176 49.6741L45.8051 56.9748C45.9598 57.2548 46.2589 57.4208 46.5684 57.4208H52.9528C53.2725 57.4208 53.5613 57.2548 53.716 56.9748C53.9017 56.6948 53.9017 56.363 53.7366 56.083Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_5273_22558">
<rect width="81" height="84" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,11 @@
<svg width="81" height="84" viewBox="0 0 81 84" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5273_21928)">
<path d="M79.7186 28.6019C82.1218 21.073 80.6778 6.03601 76.0158 0.487861C75.4073 -0.238064 74.2624 -0.134361 73.757 0.664158L68.0121 9.72786C66.5887 11.5427 64.0308 11.9575 62.1124 10.6923C55.8827 6.59601 48.4359 4.21082 40.4322 4.21082C32.4285 4.21082 24.9817 6.59601 18.752 10.6923C16.8336 11.9575 14.2757 11.5323 12.8523 9.72786L7.10738 0.664158C6.60199 -0.134361 5.45712 -0.238064 4.84859 0.487861C0.186621 6.03601 -1.25735 21.073 1.14583 28.6019C1.94002 31.1012 2.16693 33.7456 1.69248 36.3279C1.22834 38.879 0.753897 41.9693 0.753897 44.1056C0.753897 66.1323 18.5251 84.0004 40.4322 84.0004C62.3497 84.0004 80.1105 66.1427 80.1105 44.1056C80.1105 41.959 79.6464 38.879 79.1719 36.3279C78.6975 33.7456 78.9244 31.1012 79.7186 28.6019ZM40.4322 75.0819C23.4965 75.0819 9.71684 61.2271 9.71684 44.199C9.71684 43.639 9.73747 43.0893 9.7581 42.5397C10.3769 30.9353 17.3802 21.0108 27.3024 16.2819C31.2836 14.3738 35.7393 13.316 40.4322 13.316C45.1251 13.316 49.5808 14.3842 53.5724 16.2923C63.4945 21.0212 70.4978 30.9456 71.1166 42.5397C71.1476 43.0893 71.1579 43.639 71.1579 44.199C71.1476 61.2271 57.3679 75.0819 40.4322 75.0819Z" fill="#1EB4D4"/>
<path d="M53.7371 56.083L45.8881 42.4044L39.153 30.997C38.9983 30.7274 38.7095 30.5615 38.3898 30.5615H31.9538C31.634 30.5615 31.3452 30.7378 31.1905 31.0074C31.0358 31.2874 31.0358 31.6296 31.2008 31.8993L37.6368 42.7881L28.9936 56.0415C28.8183 56.3111 28.7977 56.6637 28.9524 56.9541C29.1071 57.2444 29.4062 57.4207 29.7259 57.4207H36.2032C36.5023 57.4207 36.7808 57.2652 36.9458 57.0163L41.6181 49.6741L45.8056 56.9748C45.9603 57.2548 46.2594 57.4207 46.5688 57.4207H52.9533C53.273 57.4207 53.5618 57.2548 53.7165 56.9748C53.9022 56.6948 53.9022 56.363 53.7371 56.083Z" fill="#1EB4D4"/>
</g>
<defs>
<clipPath id="clip0_5273_21928">
<rect width="81" height="84" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,4 @@
export { FormDecorator } from './react-hook-form';
export { ReactQueryDecorator } from './react-query';
export { ReduxDecorator } from './redux-decorator';
export { ConsoleTypeDecorator } from './console-type/console-type';

View File

@ -369,7 +369,6 @@
"remark-slug": "6.1.0",
"start-server-and-test": "^1.15.3",
"storybook": "^7.0.7",
"storybook-addon-console-env": "^2.0.11",
"storybook-dark-mode": "^1.1.0",
"stream-browserify": "^3.0.0",
"stream-http": "^3.2.0",

View File

@ -19621,7 +19621,6 @@ __metadata:
sql-formatter: 2.3.3
start-server-and-test: ^1.15.3
storybook: ^7.0.7
storybook-addon-console-env: ^2.0.11
storybook-dark-mode: ^1.1.0
stream-browserify: ^3.0.0
stream-http: ^3.2.0
@ -33263,23 +33262,6 @@ __metadata:
languageName: node
linkType: hard
"storybook-addon-console-env@npm:^2.0.11":
version: 2.0.11
resolution: "storybook-addon-console-env@npm:2.0.11"
dependencies:
"@storybook/blocks": 7.0.7
prop-types: 15.7.2
peerDependencies:
"@storybook/addons": ^7.0.7
"@storybook/api": ^7.0.7
"@storybook/components": ^7.0.7
"@swc/helpers": ^0.5.1
react: 17.0.2
react-dom: 17.0.2
checksum: e8b065c1ae9d7709fee4534891aa23ea566d1d9ab6d2c35d9ab48c14f4cd4f5e3b7c0ecf5d263193f64826495ce1a55309b78c2228f9c87e1126a836bbd28017
languageName: node
linkType: hard
"storybook-dark-mode@npm:^1.1.0":
version: 1.1.2
resolution: "storybook-dark-mode@npm:1.1.2"