Skip to content

Commit

Permalink
chore: preparation to NPM publish (#1511)
Browse files Browse the repository at this point in the history
  • Loading branch information
vtsvetkov-splunk authored Jan 10, 2025
1 parent 15b4e7a commit f7254db
Show file tree
Hide file tree
Showing 20 changed files with 221 additions and 107 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/build-ui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
run: yarn run lint
- name: Unit test
run: yarn run test
- name: Build UCC library
run: yarn run build:lib
- name: Build UCC UI
run: yarn run build
- name: List deps into dependencies.txt
Expand Down
27 changes: 22 additions & 5 deletions ui/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
{
"name": "@splunk/ucc_ui_lib",
"version": "0.0.1",
"name": "@splunk/add-on-ucc-framework",
"description": "UCC framework is a build and code generation framework for building Splunk Add-ons (TAs)",
"repository": {
"type": "git",
"url": "git+https://github.com/splunk/addonfactory-ucc-generator.git",
"directory": "ui"
},
"version": "5.56.0",
"license": "Apache-2.0",
"private": true,
"author": "Splunk Inc.",
"homepage": "https://splunk.github.io/addonfactory-ucc-generator",
"scripts": {
"build": "cross-env NODE_ENV=production webpack --bail",
"build:lib": "cross-env NODE_ENV=production tsc --project tsconfig.lib.json",
"build:watch": "webpack --watch",
"format": "prettier \"src/**/*.(js|jsx|ts|tsx|css)\" --write",
"format:verify": "prettier \"src/**/*.(js|jsx|ts|tsx|css)\" --list-different",
Expand All @@ -18,7 +26,8 @@
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"test-storybook": "test-storybook",
"test-storybook:update-snapshots": "yarn run test-storybook -u"
"test-storybook:update-snapshots": "yarn run test-storybook -u",
"prepublishOnly": "yarn run build:lib"
},
"dependencies": {
"@splunk/dashboard-action-buttons": "^28.0.0",
Expand Down Expand Up @@ -119,6 +128,10 @@
"webpack-dev-server": "^5.2.0",
"webpack-merge": "^6.0.1"
},
"peerDependencies": {
"react": "^16.14.0",
"typescript": "^5.6.3"
},
"resolutions": {
"@npmcli/git": "^2.1.0",
"@types/react": "^16.14.62",
Expand All @@ -139,5 +152,9 @@
"workerDirectory": [
"src/public"
]
}
},
"types": "./dist/lib/publicApi.d.ts",
"files": [
"dist/lib"
]
}
12 changes: 6 additions & 6 deletions ui/src/components/BaseFormView/BaseFormView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,10 @@ import {
UtilControlWrapper,
ServiceGroup,
OauthConfiguration,
CustomHook,
AnyEntity,
OAuthEntity,
BasicEntity,
ChangeRecord,
CustomHookClass,
EntitiesAllowingModifications,
} from '../../types/components/BaseFormTypes';
import {
Expand All @@ -51,6 +49,7 @@ import {
} from '../FormModifications/FormModifications';
import { GlobalConfig } from '../../types/globalConfig/globalConfig';
import { shouldHideForPlatform } from '../../util/pageContext';
import { CustomHookConstructor, CustomHookInstance } from '../../types/components/CustomHookClass';

function onCustomHookError(params: { methodName: string; error?: CustomHookError }) {
// eslint-disable-next-line no-console
Expand Down Expand Up @@ -107,7 +106,7 @@ class BaseFormView extends PureComponent<BaseFormProps, BaseFormState> {

datadict: Record<string, AcceptableFormValueOrNullish>;

hook?: CustomHook;
hook?: CustomHookInstance;

// eslint-disable-next-line camelcase
state_enabled?: boolean;
Expand Down Expand Up @@ -1072,7 +1071,7 @@ class BaseFormView extends PureComponent<BaseFormProps, BaseFormState> {
if (type === 'external') {
import(/* webpackIgnore: true */ `${getBuildDirPath()}/custom/${module}.js`).then(
(external) => {
const Hook = external.default;
const Hook = external.default as CustomHookConstructor;
this.hook = new Hook(
globalConfig,
this.props.serviceName,
Expand All @@ -1088,13 +1087,14 @@ class BaseFormView extends PureComponent<BaseFormProps, BaseFormState> {
// @ts-expect-error should be exported to other js module and imported here
__non_webpack_require__(
[`app/${this.appName}/js/build/custom/${module}`],
(Hook: CustomHookClass) => {
(Hook: CustomHookConstructor) => {
this.hook = new Hook(
globalConfig,
this.props.serviceName,
this.state,
this.props.mode,
this.util
this.util,
this.props.groupName
);
resolve(Hook);
}
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/CheckBoxComponent/CheckBoxComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import Switch from '@splunk/react-ui/Switch';
import { isFalse } from '../../util/considerFalseAndTruthy';

interface CheckBoxComponentProps {
export interface CheckBoxComponentProps {
value: 0 | 1 | boolean;
handleChange: (field: string, value: 0 | 1) => void;
field: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const Base: Story = {
utilCustomFunctions: {
setState: () => {},
setErrorFieldMsg: () => {},
clearAllErrorMsg: () => {},
clearAllErrorMsg: (state) => state,
setErrorMsg: () => {},
},
handleChange: () => {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const renderControlWrapper = (props: Partial<ControlWrapperProps>) => {
utilCustomFunctions: {
setState: () => {},
setErrorFieldMsg: () => {},
clearAllErrorMsg: () => {},
clearAllErrorMsg: (state) => state,
setErrorMsg: () => {},
},
handleChange: () => {},
Expand Down
52 changes: 19 additions & 33 deletions ui/src/components/CustomControl/CustomControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,12 @@ import { getUnifiedConfigs } from '../../util/util';
import { getBuildDirPath } from '../../util/script';
import { AcceptableFormValueOrNullish } from '../../types/components/shareableTypes';
import { UtilBaseForm } from '../../types/components/BaseFormTypes';
import { GlobalConfig } from '../../types/globalConfig/globalConfig';
import { Mode } from '../../constants/modes';
import { invariant } from '../../util/invariant';
import { CustomControlConstructor } from './CustomControlBase';
import { ControlData } from './CustomControl.types';

interface IData {
value: AcceptableFormValueOrNullish;
mode: Mode;
serviceName: string;
}

interface ICustomCompClass {
new (
config: GlobalConfig,
el: HTMLElement | undefined,
data: IData,
setValue: (field: string, newValue: AcceptableFormValueOrNullish) => void,
util: UtilBaseForm
): {
render: () => void;
validation?: (submittedField: string, submittedValue: string) => void;
};
}

interface ICustomCompProps {
data: IData;
interface Props {
data: ControlData;
field: string;
handleChange: (field: string, newValue: AcceptableFormValueOrNullish) => void;
controlOptions: { src: string; type: string };
Expand All @@ -38,37 +20,40 @@ interface ICustomCompProps {
utilCustomFunctions: UtilBaseForm;
}

interface ICustomCompState {
interface State {
loading: boolean;
}

class CustomControl extends React.Component<ICustomCompProps, ICustomCompState> {
class CustomControl extends React.Component<Props, State> {
static loadCustomControl = (
module: string,
type: string,
appName: string
): Promise<ICustomCompClass> =>
): Promise<CustomControlConstructor> =>
new Promise((resolve) => {
if (type === 'external') {
import(/* webpackIgnore: true */ `${getBuildDirPath()}/custom/${module}.js`).then(
(external) => {
const Control = external.default;
async (external) => {
const Control = external.default as CustomControlConstructor;
resolve(Control);
}
);
} else {
// @ts-expect-error typeof __non_webpack_require__ is not known during bundle
__non_webpack_require__([`app/${appName}/js/build/custom/${module}`], (Control) => {
resolve(Control);
});
__non_webpack_require__(
[`app/${appName}/js/build/custom/${module}`],
(Control: CustomControlConstructor) => {
resolve(Control);
}
);
}
});

shouldRender: boolean;

el?: HTMLElement;

constructor(props: ICustomCompProps) {
constructor(props: Props) {
super(props);
this.state = {
loading: true,
Expand All @@ -85,6 +70,7 @@ class CustomControl extends React.Component<ICustomCompProps, ICustomCompState>
this.props.controlOptions.type,
appName
).then((Control) => {
invariant(this.el !== undefined, 'Element should be defined');
const customControl = new Control(
globalConfig,
this.el,
Expand All @@ -101,7 +87,7 @@ class CustomControl extends React.Component<ICustomCompProps, ICustomCompState>
});
}

shouldComponentUpdate(_nextProps: ICustomCompProps, nextState: ICustomCompState) {
shouldComponentUpdate(_nextProps: Props, nextState: State) {
if (!nextState.loading && this.shouldRender) {
this.shouldRender = false;
return true;
Expand Down
8 changes: 8 additions & 0 deletions ui/src/components/CustomControl/CustomControl.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { AcceptableFormValueOrNullish } from '../../types/components/shareableTypes';
import { Mode } from '../../constants/modes';

export interface ControlData {
value: AcceptableFormValueOrNullish;
mode: Mode;
serviceName: string;
}
43 changes: 43 additions & 0 deletions ui/src/components/CustomControl/CustomControlBase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { GlobalConfig } from '../../types/globalConfig/globalConfig';
import { UtilBaseForm } from '../../types/components/BaseFormTypes';
import { AcceptableFormValueOrNullish } from '../../types/components/shareableTypes';
import { ControlData } from './CustomControl.types';

type ValueSetter = (newValue: AcceptableFormValueOrNullish) => void;

export type CustomControlInstance<T extends typeof CustomControlBase = typeof CustomControlBase> =
InstanceType<T>;

export type CustomControlConstructor<
T extends typeof CustomControlBase = typeof CustomControlBase
> = new (...args: ConstructorParameters<T>) => CustomControlInstance<T>;

export abstract class CustomControlBase {
protected globalConfig: GlobalConfig;

protected el: HTMLElement;

protected data: ControlData;

protected setValue: ValueSetter;

protected util: UtilBaseForm;

constructor(
globalConfig: GlobalConfig,
el: HTMLElement,
data: ControlData,
setValue: ValueSetter,
util: UtilBaseForm
) {
this.globalConfig = globalConfig;
this.el = el;
this.data = data;
this.setValue = setValue;
this.util = util;
}

abstract render(): void;

validation?(field: string, value: ControlData['value']): string | undefined;
}
16 changes: 4 additions & 12 deletions ui/src/components/CustomTab/CustomTab.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
import React, { useEffect, useRef, useState } from 'react';
import { _ } from '@splunk/ui-utils/i18n';
import { z } from 'zod';
import { getUnifiedConfigs } from '../../util/util';
import { getBuildDirPath } from '../../util/script';
import { TabSchema } from '../../types/globalConfig/pages';

type Tab = z.infer<typeof TabSchema>;
import { CustomTabConstructor } from './CustomTabBase';
import { Tab } from './CustomTab.types';

interface CustomTabProps {
tab: Tab;
}

interface ICustomTabClass {
new (tab: Tab, ref: HTMLDivElement): {
render: () => void;
};
}

const CustomTab: React.FC<CustomTabProps> = ({ tab }) => {
const [loading, setLoading] = useState(true);
const divRef = useRef<HTMLDivElement>(null);

const globalConfig = getUnifiedConfigs();
const appName = globalConfig.meta.name;

const loadCustomTab = (): Promise<ICustomTabClass> =>
const loadCustomTab = (): Promise<CustomTabConstructor> =>
new Promise((resolve) => {
if (tab.customTab?.type === 'external') {
import(
Expand All @@ -37,7 +29,7 @@ const CustomTab: React.FC<CustomTabProps> = ({ tab }) => {
// @ts-expect-error should be exported to other js module and imported here
__non_webpack_require__(
[`app/${appName}/js/build/custom/${tab.customTab?.src}`],
(Control: ICustomTabClass) => resolve(Control)
(Control: CustomTabConstructor) => resolve(Control)
);
}
});
Expand Down
4 changes: 4 additions & 0 deletions ui/src/components/CustomTab/CustomTab.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { z } from 'zod';
import { TabSchema } from '../../types/globalConfig/pages';

export type Tab = z.infer<typeof TabSchema>;
21 changes: 21 additions & 0 deletions ui/src/components/CustomTab/CustomTabBase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Tab } from './CustomTab.types';

export type CustomTabInstance<T extends typeof CustomTabBase = typeof CustomTabBase> =
InstanceType<T>;

export type CustomTabConstructor<T extends typeof CustomTabBase = typeof CustomTabBase> = new (
...args: ConstructorParameters<T>
) => CustomTabInstance<T>;

export abstract class CustomTabBase {
protected tab: Tab;

protected el: HTMLDivElement;

constructor(tab: Tab, el: HTMLDivElement) {
this.tab = tab;
this.el = el;
}

abstract render(): void;
}
2 changes: 1 addition & 1 deletion ui/src/components/DeleteModal/DeleteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const ModalWrapper = styled(Modal)`
width: 800px;
`;

interface DeleteModalProps {
export interface DeleteModalProps {
page: StandardPages;
handleRequestClose: () => void;
serviceName: string;
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/EntityPage/EntityPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { StandardPages } from '../../types/components/shareableTypes';
import PageContext from '../../context/PageContext';
import { UCCButton } from '../UCCButton/UCCButton';

interface EntityPageProps {
export interface EntityPageProps {
handleRequestClose: () => void;
serviceName: string;
mode: Mode;
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/TextAreaComponent/TextAreaComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const TextWrapper = styled(TextArea)`
width: 320px !important;
`;

interface TextAreaComponentProps {
export interface TextAreaComponentProps {
id?: string;
value: string | number;
handleChange: (field: string, value: string) => void;
Expand Down
Loading

0 comments on commit f7254db

Please sign in to comment.