Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Application settings management #1396

Merged
merged 10 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions vuu-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,58 +1,200 @@
import { getFieldName, queryClosest } from "@finos/vuu-utils";
import { useComponentCssInjection } from "@salt-ds/styles";
import { useWindow } from "@salt-ds/window";
import { FormEventHandler, HTMLAttributes, useCallback } from "react";

import applicationSettingsPanelCss from "./ApplicationSettingsPanel.css";
import { queryClosest } from "@finos/vuu-utils";
import {
Dropdown,
DropdownProps,
FormField,
FormFieldLabel,
Input,
Option,
Switch,
SwitchProps,
ToggleButton,
ToggleButtonGroup,
ToggleButtonGroupProps,
} from "@salt-ds/core";
import { useApplicationSettings } from "../application-provider";
import { useComponentCssInjection } from "@salt-ds/styles";
import { useWindow } from "@salt-ds/window";
import {
FormEventHandler,
HTMLAttributes,
SyntheticEvent,
useCallback,
} from "react";

import applicationSettingsPanelCss from "./ApplicationSettingsPanel.css";

// Schema type definitions
export type SettingsProperty<
T extends string | number | boolean | object = string
> = {
name: string;
label: string;
values?: T[] | object;
defaultValue?: T;
type: "string" | "boolean" | "number";
};

export interface SettingsSchema {
rohinp14 marked this conversation as resolved.
Show resolved Hide resolved
properties: SettingsProperty[];
}

// Determine the form control type to be displayed
export function getFormControl(
property: SettingsProperty,
changeHandler: FormEventHandler,
selectHandler: DropdownProps["onSelectionChange"],
currentValue: string | boolean | number
) {
const values = property.values;

if (values?.length !== undefined) {
// Switch for booleans
if (values?.length == 2) {
if (property.type === "boolean") {
return (
<Switch
label={property.label}
value={currentValue as SwitchProps["value"]}
onChange={changeHandler}
></Switch>
);
}
}
// Toggle Box for 1 or 2 values
if (values?.length <= 2) {
return (
<ToggleButtonGroup
value={currentValue as ToggleButtonGroupProps["value"]}
onChange={changeHandler}
>
{values?.map((value) => (
<ToggleButton key={value} value={value}>
{value}
</ToggleButton>
))}
</ToggleButtonGroup>
);

// Dropdown for more than 2 values provided
} else if (values?.length > 2) {
return (
<Dropdown
value={currentValue as DropdownProps["value"]}
onSelectionChange={selectHandler}
>
{values?.map((value) => {
if (typeof value === "object") {
return (
<Option
value={value.label}
key={value.value}
data-field={property.name}
></Option>
);
} else {
return (
<Option
value={value}
key={value}
data-field={property.name}
></Option>
);
}
})}
</Dropdown>
);
}
} else {
return <Input></Input>;
}
}

export interface ApplicationSettingsPanelProps
// Props for Panel
export interface ApplicatonSettingsPanelProps
extends HTMLAttributes<HTMLDivElement> {
settings: { [key: string]: unknown };
applicationSettingsSchema: SettingsSchema;
applicationSettings: Record<string, string | number | boolean>;
onApplicationSettingChanged: (
propertyName: string,
value: string | number | boolean
) => void;
}

// Generates application settings form component
export const SettingsForm = ({
applicationSettingsSchema,
rohinp14 marked this conversation as resolved.
Show resolved Hide resolved
applicationSettings,
onApplicationSettingChanged,
}: ApplicatonSettingsPanelProps) => {
const getFieldNameFromEventTarget = (evt: SyntheticEvent) => {
const fieldElement = queryClosest(evt.target, "[data-field]");
if (fieldElement && fieldElement.dataset.field) {
return fieldElement.dataset.field;
} else {
throw Error("data-field attribute not defined");
}
};

// Change Handler for toggle and input buttons
const onSettingChanged = useCallback<FormEventHandler>(
(event) => {
const fieldName = getFieldNameFromEventTarget(event);
onApplicationSettingChanged(fieldName, event.target.value);
},
[onApplicationSettingChanged]
);

// Change handler for selection form controls
const handleSelectionChange = useCallback(
(event: SyntheticEvent, [selected]: string[]) => {
const fieldName = getFieldNameFromEventTarget(event);
onApplicationSettingChanged(fieldName, selected);
},
[onApplicationSettingChanged]
);

return (
<div>
{applicationSettingsSchema.properties.map((property) => (
<FormField data-field={property.name} key={property.name}>
<FormFieldLabel>{property.label}</FormFieldLabel>
{getFormControl(
property,
onSettingChanged,
handleSelectionChange,
applicationSettings[property.name]
)}
</FormField>
))}
</div>
);
};

const classBase = "vuuApplicationSettingsPanel";

export const ApplicationSettingsPanel = () => {
export const ApplicationSettingsPanel = ({
applicationSettingsSchema,
applicationSettings,
onApplicationSettingChanged,
...htmlAttributes
}: ApplicatonSettingsPanelProps) => {
const targetWindow = useWindow();
rohinp14 marked this conversation as resolved.
Show resolved Hide resolved

useComponentCssInjection({
testId: "vuu-application-settings-panel",
css: applicationSettingsPanelCss,
window: targetWindow,
});

const { changeSetting, settings } = useApplicationSettings();

const onChangeToggleButton = useCallback<FormEventHandler>(
(e) => {
const button = queryClosest<HTMLButtonElement>(e.target, "button");
if (button) {
const fieldName = getFieldName(button);
const { value } = button;
changeSetting(fieldName, value);
}
},
[changeSetting]
);

return (
<div className={classBase}>
<FormField data-field="themeMode">
<FormFieldLabel>Light or Dark Mode</FormFieldLabel>
<ToggleButtonGroup
onChange={onChangeToggleButton}
value={settings.themeMode}
>
<ToggleButton value="light">Light</ToggleButton>
<ToggleButton value="dark">Dark</ToggleButton>
</ToggleButtonGroup>
</FormField>
<div {...htmlAttributes} className={classBase}>
<SettingsForm
applicationSettingsSchema={applicationSettingsSchema}
applicationSettings={applicationSettings}
onApplicationSettingChanged={onApplicationSettingChanged}
/>
</div>
);
};

export default ApplicationSettingsPanel;
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { SettingsSchema } from "@finos/vuu-shell";

export const applicationSettingsSchema: SettingsSchema = {
properties: [
{
name: "themeMode",
label: "Mode",
values: ["light", "dark"],
defaultValue: "light",
type: "string",
},
{
name: "dateFormatPattern",
label: "Date Formatting",
values: ["dd/mm/yyyy", "mm/dd/yyyy", "dd MMMM yyyy"],
defaultValue: "dd/mm/yyyy",
type: "string",
},
{
name: "region",
label: "Region",
values: [
{ value: "us", label: "US" },
{ value: "apac", label: "Asia Pacific" },
{ value: "emea", label: "Europe, Middle East & Africa" },
],
defaultValue: "apac",
type: "string",
},
{
name: "greyscale",
label: "Greyscale",
values: [true, false],
defaultValue: false,
type: "boolean",
},
],
};

export default applicationSettingsSchema;
1 change: 1 addition & 0 deletions vuu-ui/packages/vuu-shell/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from "./shell-layouts/side-panel";
export * from "./ShellContextProvider";
export * from "./feature-list";
export * from "./theme-switch";
export * from "./application-settings";
5 changes: 5 additions & 0 deletions vuu-ui/showcase/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script>
if (window.parent !== window) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
}
</script>
</body>
</html>
Loading
Loading