Skip to content

Commit

Permalink
[Feature] [WRS-2233] Add variable focus and blur listeners in Studio …
Browse files Browse the repository at this point in the history
…UI Integration layer (#260)
  • Loading branch information
abdelhalimkhouas authored Jan 28, 2025
1 parent 2633099 commit 6ec91d3
Show file tree
Hide file tree
Showing 16 changed files with 294 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/MainContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ function MainContent({ projectConfig, updateToken: setAuthToken }: MainContentPr

useEffect(() => {
if (!multiLayoutMode && isDocumentLoaded) zoomToPage();
}, [multiLayoutMode, isDocumentLoaded, zoomToPage]);
}, [isDocumentLoaded, multiLayoutMode, zoomToPage]);

const navbarProps = useMemo(
() => ({
Expand Down
4 changes: 4 additions & 0 deletions src/_dev-execution/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,9 @@ import { TokenManager } from './token-manager';
featureFlags: {
STUDIO_DATA_SOURCE: true,
},
// eslint-disable-next-line no-console
onVariableFocus: (id) => console.log('focused var: ', id),
// eslint-disable-next-line no-console
onVariableBlur: (id) => console.log('blurred var: ', id),
});
})();
10 changes: 8 additions & 2 deletions src/components/imagePanel/ImagePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { Media, MediaDownloadType } from '@chili-publish/studio-sdk';
import { convertToPreviewType } from '../../utils/mediaUtils';
import ItemBrowser from '../itemBrowser/ItemBrowser';
import { useVariablePanelContext } from '../../contexts/VariablePanelContext';
import { useUiConfigContext } from '../../contexts/UiConfigContext';

function ImagePanel() {
const { handleUpdateImage, currentVariableConnectorId } = useVariablePanelContext();
const { handleUpdateImage, currentVariableConnectorId, currentVariableId } = useVariablePanelContext();
const { onVariableBlur } = useUiConfigContext();

const previewCall = (id: string): Promise<Uint8Array> =>
window.StudioUISDK.mediaConnector.download(currentVariableConnectorId, id, MediaDownloadType.mediumres, {});

Expand All @@ -17,7 +20,10 @@ function ImagePanel() {
queryCall={window.StudioUISDK.mediaConnector.query}
previewCall={previewCall}
onSelect={(assets) => {
if (assets.length > 0) handleUpdateImage(assets[0]);
if (assets.length > 0) {
handleUpdateImage(assets[0]);
onVariableBlur?.(currentVariableId);
}
}}
convertToPreviewType={convertToPreviewType}
/>
Expand Down
6 changes: 6 additions & 0 deletions src/components/shared/StudioDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface StudioDropdownProps {
validationError?: string;
required?: boolean;
onChange?: (_: string) => void;
onMenuOpen?: () => void;
onMenuClose?: () => void;
}
function StudioDropdown({
dataId,
Expand All @@ -22,6 +24,8 @@ function StudioDropdown({
validationError,
required,
onChange,
onMenuOpen,
onMenuClose,
}: StudioDropdownProps) {
return (
<Select
Expand All @@ -36,6 +40,8 @@ function StudioDropdown({
required={required}
label={label}
validationErrorMessage={validationError}
onMenuOpen={onMenuOpen}
onMenuClose={onMenuClose}
/>
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/components/variablesComponents/BooleanVariable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import { useEffect, useState } from 'react';
import { getDataIdForSUI, getDataTestIdForSUI } from '../../utils/dataIds';
import { BooleanVariableContainer, HelpTextWrapper } from './VariablesComponents.styles';
import { IBooleanVariable } from './VariablesComponents.types';
import { useUiConfigContext } from '../../contexts/UiConfigContext';

function BooleanVariable(props: IBooleanVariable) {
const { variable, handleValueChange } = props;
const { onVariableBlur, onVariableFocus } = useUiConfigContext();

const [toggled, setToggled] = useState((variable as BooleanVariable).value);

useEffect(() => {
Expand All @@ -28,6 +31,8 @@ function BooleanVariable(props: IBooleanVariable) {
onChange={(val: boolean) => {
handleValueChange(val);
setToggled(val);
onVariableFocus?.(variable.id);
onVariableBlur?.(variable.id);
}}
noLabelHeight
/>
Expand Down
4 changes: 4 additions & 0 deletions src/components/variablesComponents/NumberVariable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { ChangeEvent } from 'react';
import { getDataIdForSUI, getDataTestIdForSUI } from '../../utils/dataIds';
import { HelpTextWrapper } from './VariablesComponents.styles';
import { INumberVariable } from './VariablesComponents.types';
import { useUiConfigContext } from '../../contexts/UiConfigContext';

function NumberVariable(props: INumberVariable) {
const { variable, validationError, onValueChange } = props;
const { onVariableBlur, onVariableFocus } = useUiConfigContext();

return (
<HelpTextWrapper>
Expand All @@ -23,10 +25,12 @@ function NumberVariable(props: INumberVariable) {
dataId={getDataIdForSUI(`input-number-${variable.id}`)}
dataTestId={getDataTestIdForSUI(`input-number-${variable.id}`)}
dataIntercomId={`input-variable-${variable.name}`}
onFocus={() => onVariableFocus?.(variable.id)}
onBlur={(event: ChangeEvent<HTMLInputElement>) => {
const currentValue = parseFloat(event.target.value.replace(',', '.'));
const prevValue = variable.value;
onValueChange(currentValue, { changed: prevValue !== currentValue });
onVariableBlur?.(variable.id);
}}
onValueChange={(value: string) => {
const currentValue = parseFloat(value.replace(',', '.'));
Expand Down
4 changes: 4 additions & 0 deletions src/components/variablesComponents/TextVariable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { getDataIdForSUI, getDataTestIdForSUI } from '../../utils/dataIds';
import { HelpTextWrapper } from './VariablesComponents.styles';
import { ITextVariable } from './VariablesComponents.types';
import { getVariablePlaceholder } from './variablePlaceholder.util';
import { useUiConfigContext } from '../../contexts/UiConfigContext';

function TextVariable(props: ITextVariable) {
const { variable, validationError, onValueChange } = props;
const { onVariableBlur, onVariableFocus } = useUiConfigContext();

const [variableValue, setVariableValue] = useState(
(variable as ShortTextVariable).value || (variable as LongTextVariable).value,
Expand All @@ -33,10 +35,12 @@ function TextVariable(props: ITextVariable) {
placeholder={placeholder}
required={variable.isRequired}
onChange={handleVariableChange}
onFocus={() => onVariableFocus?.(variable.id)}
onBlur={(event: ChangeEvent<HTMLInputElement>) => {
const oldValue = (variable as ShortTextVariable).value || (variable as LongTextVariable).value;
const newValue = event.target.value;
onValueChange(newValue, { changed: oldValue !== newValue });
onVariableBlur?.(variable.id);
}}
name={variable.id}
label={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import useDateVariable from '../useDateVariable';
import { getVariablePlaceholder } from '../variablePlaceholder.util';
import { HelpTextWrapper } from '../VariablesComponents.styles';
import { IDateVariable } from '../VariablesComponents.types';
import { useUiConfigContext } from '../../../contexts/UiConfigContext';

function DateVariable(props: IDateVariable) {
const {
Expand All @@ -20,6 +21,8 @@ function DateVariable(props: IDateVariable) {
onBlur,
} = props;

const { onVariableBlur, onVariableFocus } = useUiConfigContext();

const { minDate, maxDate } = useDateVariable(variable);
const isMobileSize = useMobileSize();

Expand Down Expand Up @@ -64,14 +67,18 @@ function DateVariable(props: IDateVariable) {
onBlur={() => {
const selectedDate = getSelectedDate;
onBlur?.(selectedDate ? formatDate(selectedDate) : '');
onVariableBlur?.(variable.id);
}}
selected={getSelectedDate}
dataId={getDataIdForSUI(`${variable.id}-variable-date-picker`)}
dataTestId={getDataTestIdForSUI(`${variable.id}-variable-date-picker`)}
placeholder={placeholder}
minDate={minDate}
maxDate={maxDate}
onCalendarOpen={() => isMobileSize && onCalendarOpen?.(variable)}
onCalendarOpen={() => {
onVariableFocus?.(variable.id);
if (isMobileSize) onCalendarOpen?.(variable);
}}
inline={inline}
excludedDays={variable.excludedDays}
validationErrorMessage={validationError}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import { getVariablePlaceholder } from '../variablePlaceholder.util';
import { useMediaDetails } from './useMediaDetails';
import { usePreviewImageUrl } from './usePreviewImageUrl';
import { useVariableConnector } from './useVariableConnector';
import { useUiConfigContext } from '../../../contexts/UiConfigContext';

function ImageVariable(props: IImageVariable) {
const { variable, validationError, handleImageRemove } = props;
const { onVariableFocus, onVariableBlur } = useUiConfigContext();

const placeholder = getVariablePlaceholder(variable);

Expand Down Expand Up @@ -52,7 +54,11 @@ function ImageVariable(props: IImageVariable) {
placeholder={placeholder}
errorMsg="Something went wrong. Please try again"
previewImage={previewImage}
onRemove={handleImageRemove}
onRemove={() => {
handleImageRemove();
onVariableFocus?.(variable.id);
onVariableBlur?.(variable.id);
}}
onBrowse={async () => {
if (!selectedConnector) {
throw new Error('There is no selected connector');
Expand All @@ -61,6 +67,7 @@ function ImageVariable(props: IImageVariable) {
if (variable.value?.connectorId && isAuthenticationRequired(selectedConnector)) {
await verifyAuthentication(variable.value.connectorId);
}
onVariableFocus?.(variable.id);
showImagePanel(variable);
} catch (error) {
// TODO: We should handle connector's authorization issue accordingly
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { InputLabel, SelectOptions } from '@chili-publish/grafx-shared-components';
import { useUiConfigContext } from '../../../contexts/UiConfigContext';
import StudioDropdown from '../../shared/StudioDropdown';
import { ComponentWrapper } from '../../variables/VariablesPanel.styles';
import { getVariablePlaceholder } from '../variablePlaceholder.util';
Expand All @@ -7,6 +8,7 @@ import { IListVariable } from '../VariablesComponents.types';

function ListVariable(props: IListVariable) {
const { variable, validationError, onChange } = props;
const { onVariableBlur, onVariableFocus } = useUiConfigContext();

const options = variable.items.map((item) => ({
label: item.displayValue || item.value,
Expand Down Expand Up @@ -41,6 +43,8 @@ function ListVariable(props: IListVariable) {
required={variable.isRequired}
validationError={validationError}
onChange={(val) => updateVariableValue(variable.id, val)}
onMenuOpen={() => onVariableFocus?.(variable.id)}
onMenuClose={() => onVariableBlur?.(variable.id)}
/>
</div>
{variable.helpText && !validationError ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ListVariable } from '@chili-publish/studio-sdk/lib/src/next';
import { getVariablePlaceholder } from '../variablePlaceholder.util';
import StudioMobileDropdown from '../../shared/StudioMobileDropdown/StudioMobileDropdown';
import { getDataIdForSUI } from '../../../utils/dataIds';
import { useUiConfigContext } from '../../../contexts/UiConfigContext';

interface MobileListVariableProps {
variable: ListVariable;
Expand All @@ -24,6 +25,8 @@ function MobileListVariable({
onMenuClose,
onItemSelected,
}: MobileListVariableProps) {
const { onVariableBlur, onVariableFocus } = useUiConfigContext();

const options = variable.items.map((item) => ({
label: item.displayValue || item.value,
value: item.value,
Expand Down Expand Up @@ -52,8 +55,14 @@ function MobileListVariable({
required={required}
validationError={validationError}
onChange={updateVariableValue}
onMenuOpen={onMenuOpen}
onMenuClose={onMenuClose}
onMenuOpen={() => {
if (onMenuOpen) onMenuOpen();
onVariableFocus?.(variable.id);
}}
onMenuClose={() => {
if (onMenuClose) onMenuClose();
onVariableBlur?.(variable.id);
}}
/>
);
}
Expand Down
8 changes: 7 additions & 1 deletion src/contexts/UiConfigContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export const UiConfigContextDefaultValues: IUiConfigContext = {
userInterfaces: [],
selectedUserInterfaceId: '',
onUserInterfaceChange: () => null,
onVariableFocus: () => null,
onVariableBlur: () => null,
};

export const UiConfigContext = createContext<IUiConfigContext>(UiConfigContextDefaultValues);
Expand Down Expand Up @@ -91,13 +93,17 @@ export function UiConfigContextProvider({
userInterfaceOutputSettings,
isDownloadBtnVisible: projectConfig.uiOptions.widgets?.downloadButton?.visible ?? false,
isBackBtnVisible: projectConfig.uiOptions.widgets?.backButton?.visible ?? false,
onVariableFocus: projectConfig.onVariableFocus,
onVariableBlur: projectConfig.onVariableBlur,
}),
[
userInterfaces,
selectedUserInterfaceId,
projectConfig.outputSettings,
projectConfig.uiOptions,
projectConfig.outputSettings,
projectConfig.graFxStudioEnvironmentApiBaseUrl,
projectConfig.onVariableFocus,
projectConfig.onVariableBlur,
userInterfaceOutputSettings,
],
);
Expand Down
2 changes: 2 additions & 0 deletions src/contexts/UiConfigContext.types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ export interface IUiConfigContext {
userInterfaces: UserInterface[];
selectedUserInterfaceId: string | null;
onUserInterfaceChange: (_: string) => void;
onVariableFocus?: (id: string) => void;
onVariableBlur?: (id: string) => void;
}
12 changes: 12 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ export default class StudioUI {
onConnectorAuthenticationRequested?: (connectorId: string) => Promise<ConnectorAuthenticationResult>,
customElement?: HTMLElement | string,
onSetMultiLayout?: (setMultiLayout: React.Dispatch<React.SetStateAction<boolean>>) => void,
onVariableFocus?: (variableId: string) => void,
onVariableBlur?: (variableId: string) => void,
graFxStudioEnvironmentApiBaseUrl = '',
) {
return new StudioUI(selector, {
Expand Down Expand Up @@ -157,6 +159,8 @@ export default class StudioUI {
onConnectorAuthenticationRequested,
customElement,
onSetMultiLayout,
onVariableFocus,
onVariableBlur,
});
}

Expand Down Expand Up @@ -200,6 +204,8 @@ export default class StudioUI {
onConnectorAuthenticationRequested,
customElement,
onSetMultiLayout,
onVariableFocus,
onVariableBlur,
} = config;
const projectLoader = new StudioProjectLoader(
projectId,
Expand Down Expand Up @@ -240,6 +246,8 @@ export default class StudioUI {
onConnectorAuthenticationRequested,
customElement,
onSetMultiLayout,
onVariableFocus,
onVariableBlur,
graFxStudioEnvironmentApiBaseUrl,
);
}
Expand Down Expand Up @@ -365,6 +373,8 @@ export default class StudioUI {
onConnectorAuthenticationRequested,
customElement,
onSetMultiLayout,
onVariableFocus,
onVariableBlur,
} = config;

const projectLoader = new StudioProjectLoader(
Expand Down Expand Up @@ -404,6 +414,8 @@ export default class StudioUI {
onConnectorAuthenticationRequested,
customElement,
onSetMultiLayout,
onVariableFocus,
onVariableBlur,
graFxStudioEnvironmentApiBaseUrl,
);
}
Expand Down
Loading

0 comments on commit 6ec91d3

Please sign in to comment.