Skip to content

Commit

Permalink
[Fix] Load user interfaces on demand
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandraFlavia9 committed Jan 31, 2025
1 parent 9b9ba63 commit ec46b7c
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 268 deletions.
30 changes: 19 additions & 11 deletions src/MainContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { SuiCanvas } from './MainContent.styles';
import { Project, ProjectConfig } from './types/types';
import { APP_WRAPPER_ID } from './utils/constants';
import { getDataIdForSUI, getDataTestIdForSUI } from './utils/dataIds';
import { OutputSettingsContextProvider } from './components/navbar/OutputSettingsContext';

declare global {
interface Window {
Expand Down Expand Up @@ -117,7 +118,7 @@ function MainContent({ projectConfig, updateToken: setAuthToken }: MainContentPr
})
.catch((err: Error) => {
// eslint-disable-next-line no-console
console.error(`[${MainContent.name}] Error`, err);
console.log(`[${MainContent.name}]`, err);
return err;
});
}, [projectConfig.onProjectInfoRequested, projectConfig.projectId, projectConfig]);
Expand Down Expand Up @@ -281,7 +282,7 @@ function MainContent({ projectConfig, updateToken: setAuthToken }: MainContentPr
})
.catch((err: Error) => {
// eslint-disable-next-line no-console
console.error(`[${MainContent.name}] Error`, err);
console.log(`[${MainContent.name}]`, err);
return err;
});

Expand Down Expand Up @@ -371,20 +372,27 @@ function MainContent({ projectConfig, updateToken: setAuthToken }: MainContentPr
<AppProvider isDocumentLoaded={isDocumentLoaded} isAnimationPlaying={animationStatus} dataSource={dataSource}>
<ShortcutProvider projectConfig={projectConfig} undoStackState={undoStackState} zoom={currentZoom}>
<Container>
<UiConfigContextProvider projectConfig={projectConfig} layoutIntent={layoutIntent}>
<UiConfigContextProvider projectConfig={projectConfig}>
<VariablePanelContextProvider
connectors={{ mediaConnectors, fontsConnectors }}
variables={variables}
>
<div id={APP_WRAPPER_ID} className="app">
{projectConfig.sandboxMode ? (
<UiThemeProvider theme="studio" mode="dark">
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<StudioNavbar {...navbarProps} />
</UiThemeProvider>
) : (
// eslint-disable-next-line react/jsx-props-no-spreading
<Navbar {...navbarProps} />
{projectConfig.uiOptions.widgets?.navBar?.visible === false ? null : (
<OutputSettingsContextProvider
projectConfig={projectConfig}
layoutIntent={layoutIntent}
>
{projectConfig.sandboxMode ? (
<UiThemeProvider theme="studio" mode="dark">
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<StudioNavbar {...navbarProps} />
</UiThemeProvider>
) : (
// eslint-disable-next-line react/jsx-props-no-spreading
<Navbar {...navbarProps} />
)}
</OutputSettingsContextProvider>
)}

<MainContentContainer
Expand Down
3 changes: 0 additions & 3 deletions src/components/navbar/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { css } from 'styled-components';
import { useUiConfigContext } from '../../contexts/UiConfigContext';
import { getDataIdForSUI, getDataTestIdForSUI } from '../../utils/dataIds';
import { NavbarItem, StyledNavbar } from './Navbar.styles';
import { INavbar } from './Navbar.types';
Expand All @@ -9,7 +8,6 @@ import useNavbar from './useNavbar';

function Navbar(props: INavbar) {
const { projectName, goBack, projectConfig, zoom, undoStackState } = props;
const { uiOptions } = useUiConfigContext();

const { isDownloadPanelVisible, showDownloadPanel, hideDownloadPanel, handleDownload } = useDownloadPanel(
projectConfig,
Expand All @@ -24,7 +22,6 @@ function Navbar(props: INavbar) {
onDownloadPanelOpen: showDownloadPanel,
});

if (uiOptions.widgets?.navBar?.visible === false) return null;
return (
<StyledNavbar
data-id={getDataIdForSUI('navbar')}
Expand Down
82 changes: 82 additions & 0 deletions src/components/navbar/OutputSettingsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { ReactNode, createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import {
ProjectConfig,
UserInterfaceOutputSettings,
UserInterfaceWithOutputSettings,
defaultOutputSettings,
} from '../../types/types';
import { IOutputSettingsContext } from './OutputSettingsContext.types';
import { useAppContext } from '../../contexts/AppProvider';
import { OutputType } from '../../utils/ApiTypes';

export const OutputSettingsContextDefaultValues: IOutputSettingsContext = {
selectedUserInterfaceId: '',
outputSettings: defaultOutputSettings,
userInterfaceOutputSettings: null,
onUserInterfaceChange: () => null,
};

export const OutputSettingsContext = createContext<IOutputSettingsContext>(OutputSettingsContextDefaultValues);

export const useOutputSettingsContext = () => {
return useContext(OutputSettingsContext);
};

export function OutputSettingsContextProvider({
projectConfig,
layoutIntent,
children,
}: {
projectConfig: ProjectConfig;
layoutIntent: string | null;
children: ReactNode;
}) {
const { dataSource } = useAppContext();

const [selectedUserInterfaceId, setSelectedUserInterfaceId] = useState<string | null>(
projectConfig.userInterfaceID || null,
);
const [userInterfaceOutputSettings, setUserInterfaceOutputSettings] = useState<
UserInterfaceOutputSettings[] | null
>([]);

const fetchOutputSettings = useCallback(
async (userInterfaceId?: string) => {
if (projectConfig.onFetchOutputSettings) {
projectConfig
.onFetchOutputSettings(userInterfaceId)
.then((res: UserInterfaceWithOutputSettings | null) => {
let settings = res?.outputSettings?.filter((val) =>
val.layoutIntents.includes(layoutIntent ?? ''),
);

settings = dataSource ? settings : settings?.filter((s) => s.outputType !== OutputType.Batch);
setUserInterfaceOutputSettings(settings ?? null);
setSelectedUserInterfaceId(res?.userInterface?.id || null);
});
}
},
[layoutIntent, projectConfig, dataSource],
);

useEffect(() => {
fetchOutputSettings(selectedUserInterfaceId || undefined);
}, [selectedUserInterfaceId, fetchOutputSettings]);

const data = useMemo(
() => ({
selectedUserInterfaceId,
outputSettings: projectConfig.outputSettings,
userInterfaceOutputSettings,
onUserInterfaceChange: setSelectedUserInterfaceId,
}),
[
selectedUserInterfaceId,
userInterfaceOutputSettings,
projectConfig.outputSettings,
setSelectedUserInterfaceId,
],
);

return <OutputSettingsContext.Provider value={data}>{children}</OutputSettingsContext.Provider>;
}
8 changes: 8 additions & 0 deletions src/components/navbar/OutputSettingsContext.types.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { OutputSettings, UserInterfaceOutputSettings } from '../../types/types';

export interface IOutputSettingsContext {
outputSettings: OutputSettings;
userInterfaceOutputSettings: UserInterfaceOutputSettings[] | null;
selectedUserInterfaceId: string | null;
onUserInterfaceChange: (_: string) => void;
}
4 changes: 2 additions & 2 deletions src/components/navbar/downloadPanel/useDownload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { AvailableIcons, SelectOptions, useOnClickOutside } from '@chili-publish
import { DownloadFormats } from '@chili-publish/studio-sdk';
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';
import DropdownOption from './DropdownOption';
import { useUiConfigContext } from '../../../contexts/UiConfigContext';
import { UserInterfaceOutputSettings } from '../../../types/types';
import { outputTypesIcons } from './DownloadPanel.types';
import { useOutputSettingsContext } from '../OutputSettingsContext';

const useDownload = (hideDownloadPanel: () => void) => {
const { outputSettings, userInterfaceOutputSettings } = useUiConfigContext();
const { outputSettings, userInterfaceOutputSettings } = useOutputSettingsContext();
const initialDownloadState: Record<DownloadFormats, boolean> = {
[DownloadFormats.JPG]: false,
[DownloadFormats.PNG]: false,
Expand Down
6 changes: 4 additions & 2 deletions src/components/navbar/navbarItems/useNavbarDownloadBtn.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { useMemo } from 'react';
import { AvailableIcons, ButtonVariant, useMobileSize } from '@chili-publish/grafx-shared-components';
import { useUiConfigContext } from '../../../contexts/UiConfigContext';
import NavbarButton from '../../navbarButton/NavbarButton';
import { NavbarLabel } from '../Navbar.styles';
import { useUiConfigContext } from '../../../contexts/UiConfigContext';
import { useOutputSettingsContext } from '../OutputSettingsContext';

const useNavbarDownloadBtn = (onDownloadPanelOpen: () => void) => {
const { isDownloadBtnVisible, userInterfaceOutputSettings } = useUiConfigContext();
const { isDownloadBtnVisible } = useUiConfigContext();
const { userInterfaceOutputSettings } = useOutputSettingsContext();
const isMobile = useMobileSize();

const navbarItem = useMemo(
Expand Down
16 changes: 14 additions & 2 deletions src/components/navbar/navbarItems/useUserInterfaceSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { AvailableIcons, Select } from '@chili-publish/grafx-shared-components';
import { useMemo } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { SESSION_USER_INTEFACE_ID_KEY } from '../../../utils/constants';
import { getDataIdForSUI, getDataTestIdForSUI } from '../../../utils/dataIds';
import { useOutputSettingsContext } from '../OutputSettingsContext';
import { UserInterface } from '../../../types/types';
import { useUiConfigContext } from '../../../contexts/UiConfigContext';

const useUserInterfaceSelector = () => {
const { selectedUserInterfaceId, userInterfaces, onUserInterfaceChange } = useUiConfigContext();
const { projectConfig } = useUiConfigContext();
const [userInterfaces, setUserInterfaces] = useState<UserInterface[]>([]);
const { selectedUserInterfaceId, onUserInterfaceChange } = useOutputSettingsContext();

useEffect(() => {
if (projectConfig.onFetchUserInterfaces) {
projectConfig.onFetchUserInterfaces().then((res) => {
setUserInterfaces(res?.data?.data || []);
});
}
}, [projectConfig]);

const options = useMemo(
() => userInterfaces.map((item) => ({ label: item.name, value: item.id })),
Expand Down
80 changes: 5 additions & 75 deletions src/contexts/UiConfigContext.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
import { ReactNode, createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import {
ProjectConfig,
UserInterface,
UserInterfaceOutputSettings,
UserInterfaceWithOutputSettings,
defaultOutputSettings,
defaultUiOptions,
} from '../types/types';
import { OutputType } from '../utils/ApiTypes';
import { useAppContext } from './AppProvider';
import { ReactNode, createContext, useContext, useMemo } from 'react';
import { ProjectConfig, defaultUiOptions } from '../types/types';
import { IUiConfigContext } from './UiConfigContext.types';

export const UiConfigContextDefaultValues: IUiConfigContext = {
projectConfig: {} as ProjectConfig,
uiOptions: defaultUiOptions,
outputSettings: defaultOutputSettings,
userInterfaceOutputSettings: null,
isDownloadBtnVisible: false,
isBackBtnVisible: defaultUiOptions.widgets.downloadButton?.visible || false,
graFxStudioEnvironmentApiBaseUrl: '',

userInterfaces: [],
selectedUserInterfaceId: '',
onUserInterfaceChange: () => null,
onVariableFocus: () => null,
onVariableBlur: () => null,
};
Expand All @@ -35,77 +21,21 @@ export const useUiConfigContext = () => {
export function UiConfigContextProvider({
children,
projectConfig,
layoutIntent,
}: {
children: ReactNode;
projectConfig: ProjectConfig;
layoutIntent: string | null;
}) {
const { dataSource } = useAppContext();

const [selectedUserInterfaceId, setSelectedUserInterfaceId] = useState<string | null>(
projectConfig.userInterfaceID || null,
);
const [userInterfaces, setUserInterfaces] = useState<UserInterface[]>([]);
const [userInterfaceOutputSettings, setUserInterfaceOutputSettings] = useState<
UserInterfaceOutputSettings[] | null
>([]);

const fetchOutputSettings = useCallback(
async (userInterfaceId?: string) => {
if (projectConfig.onFetchOutputSettings) {
projectConfig
.onFetchOutputSettings(userInterfaceId)
.then((res: UserInterfaceWithOutputSettings | null) => {
let settings = res?.outputSettings?.filter((val) =>
val.layoutIntents.includes(layoutIntent ?? ''),
);

settings = dataSource ? settings : settings?.filter((s) => s.outputType !== OutputType.Batch);
setUserInterfaceOutputSettings(settings ?? null);
setSelectedUserInterfaceId(res?.userInterface?.id || null);
});
}
},
[layoutIntent, projectConfig, dataSource],
);

useEffect(() => {
fetchOutputSettings(selectedUserInterfaceId || undefined);
}, [selectedUserInterfaceId, fetchOutputSettings]);

useEffect(() => {
if (projectConfig.onFetchUserInterfaces) {
projectConfig.onFetchUserInterfaces().then((res) => {
setUserInterfaces(res?.data?.data || []);
});
}
}, [projectConfig]);

const data = useMemo(
() => ({
userInterfaces,
selectedUserInterfaceId,
onUserInterfaceChange: setSelectedUserInterfaceId,
projectConfig,
uiOptions: projectConfig.uiOptions,
outputSettings: projectConfig.outputSettings,
graFxStudioEnvironmentApiBaseUrl: projectConfig.graFxStudioEnvironmentApiBaseUrl,
userInterfaceOutputSettings,
isDownloadBtnVisible: projectConfig.uiOptions.widgets?.downloadButton?.visible ?? false,
isBackBtnVisible: projectConfig.uiOptions.widgets?.backButton?.visible ?? false,
onVariableFocus: projectConfig.onVariableFocus,
onVariableBlur: projectConfig.onVariableBlur,
}),
[
userInterfaces,
selectedUserInterfaceId,
projectConfig.uiOptions,
projectConfig.outputSettings,
projectConfig.graFxStudioEnvironmentApiBaseUrl,
projectConfig.onVariableFocus,
projectConfig.onVariableBlur,
userInterfaceOutputSettings,
],
[projectConfig],
);

return <UiConfigContext.Provider value={data}>{children}</UiConfigContext.Provider>;
Expand Down
10 changes: 3 additions & 7 deletions src/contexts/UiConfigContext.types.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { OutputSettings, UiOptions, UserInterface, UserInterfaceOutputSettings } from '../types/types';
import { ProjectConfig, UiOptions } from '../types/types';

export interface IUiConfigContext {
projectConfig: ProjectConfig;

uiOptions: UiOptions;
outputSettings: OutputSettings;
userInterfaceOutputSettings: UserInterfaceOutputSettings[] | null;
isDownloadBtnVisible: boolean;
isBackBtnVisible: boolean;
graFxStudioEnvironmentApiBaseUrl: string;

userInterfaces: UserInterface[];
selectedUserInterfaceId: string | null;
onUserInterfaceChange: (_: string) => void;
onVariableFocus?: (id: string) => void;
onVariableBlur?: (id: string) => void;
}
20 changes: 9 additions & 11 deletions src/tests/integration/StudioLoaderUndefinedProject.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,17 @@ describe('StudioLoader integration - no projectId', () => {
});

it('Should not try to load project details when there is no projectId provided in the config', async () => {
const config = {
selector: 'sui-root',
projectDownloadUrl,
projectUploadUrl: `${environmentBaseURL}/projects/${projectID}`,
graFxStudioEnvironmentApiBaseUrl: environmentBaseURL,
authToken: token,
projectName,
refreshTokenAction: () => Promise.resolve(''),
};

render(<div id="sui-root" />);
act(() => {
StudioUI.studioLoaderConfig(config);
StudioUI.studioLoaderConfig({
selector: 'sui-root',
projectDownloadUrl,
projectUploadUrl: `${environmentBaseURL}/projects/${projectID}`,
graFxStudioEnvironmentApiBaseUrl: environmentBaseURL,
authToken: token,
projectName,
refreshTokenAction: () => Promise.resolve(''),
});
});

await waitFor(() => {
Expand Down
Loading

0 comments on commit ec46b7c

Please sign in to comment.