Skip to content

Commit

Permalink
feat: release v1.21.0 (#271)
Browse files Browse the repository at this point in the history
* test(UI): ADDON-58762 Jest setup for unit testing (#238)

* test(UI): ADDON-58762 Jest setup

* test(UI): ADDON-58762 Update eslintrc file

* test(UI): ADDON-58762 Minor change

* test(UI): ADDON-58762 Added test unit pipeline in the github workflow

* test(UI): ADDON-58762 Added test cases for File Validator

* test(UI): ADDON-58762 Handle file validation

* test(UI): ADDON-58762 Fixed console error if input type file with encrypted false

* test(UI): ADDON-58762 Added test:watch in the scripts

* test(UI): ADDON-58762 Remove eslint ignore line

* fix: ADDON-58705 Table action buttons are dynamic from the global config file (#239)

* fix ADDON-58705 Table action buttons are dynamic from the global config file

* fix ADDON-58705 Revert enable type in the schema file

* fix ADDON-58705 Removed commented code

* refactor: ADDON-59458 Refactored tabs feature and style changes (#240)

* test(UI): ADDON-58762 Jest setup

* test(UI): ADDON-58762 Update eslintrc file

* test(UI): ADDON-58762 Added test:watch in the scripts

* test(UI): ADDON-58762 Remove eslint ignore line

* fix ADDON-58705 Table action buttons are dynamic from the global config file

* fix ADDON-58705 Revert enable type in the schema file

* refactor: ADDON-59458 Refactored tabs feature and style changes

* feat: ADDON-59601 Added support for custom component in Configuration Tabs (#249)

* test(UI): ADDON-58762 Jest setup

* test(UI): ADDON-58762 Update eslintrc file

* test(UI): ADDON-58762 Minor change

* test(UI): ADDON-58762 Added test unit pipeline in the github workflow

* test(UI): ADDON-58762 Added test cases for File Validator

* test(UI): ADDON-58762 Handle file validation

* test(UI): ADDON-58762 Fixed console error if input type file with encrypted false

* test(UI): ADDON-58762 Added test:watch in the scripts

* test(UI): ADDON-58762 Remove eslint ignore line

* fix ADDON-58705 Table action buttons are dynamic from the global config file

* fix ADDON-58705 Revert enable type in the schema file

* fix ADDON-58705 Removed commented code

* refactor: ADDON-59458 Refactored tabs feature and style changes

* feat: ADDON-59601 Added support for custom component in Configuration Tabs

* fix: ADDON-59559 Fixed ui for more info field with textarea component (#250)

* fix: ADDON-59559 Fixed ui of more info field with textarea component

* chore(code): updating wordBreak to break-word

* fix: ADDON-59846 update the code for preventing unnecessary API call for "style=page" property (#251)

* fix: ADDON-59846 update the code for preventing unnecessary API call for style=page property

* fix: ADDON-59780 resolved the dependabot PRs (#252)

* fix: ADDON-59780 resolved dependabot PRs

* fix: ADDON-59780 resolved dependabot PRs

* fix: ADDON-59459 revert code changes of ADDON-47627 (#258)

---------

Co-authored-by: Hetang Modi <hemodi@splunk.com>

---------

Co-authored-by: Hetang Modi <hemodi@splunk.com>
Co-authored-by: Artem Rys <rysartem@gmail.com>

---------

Co-authored-by: Tushar Balar <62089106+tbalar-splunk@users.noreply.github.com>
Co-authored-by: Hetang Modi <hemodi@splunk.com>
  • Loading branch information
3 people committed Feb 9, 2023
1 parent 171d00d commit 0e5df40
Show file tree
Hide file tree
Showing 24 changed files with 3,798 additions and 2,116 deletions.
4 changes: 0 additions & 4 deletions ui/.babelrc.js

This file was deleted.

11 changes: 8 additions & 3 deletions ui/.eslintrc.js
@@ -1,7 +1,12 @@
/* eslint no-undef: "error" */
/* eslint-env node */
module.exports = {
parser: '@babel/eslint-parser',
extends: ['@splunk/eslint-config/browser', 'prettier'],
plugins: ['prettier'],
extends: ['@splunk/eslint-config/browser', 'prettier', 'plugin:jest/recommended'],
plugins: ['prettier', 'jest'],
env: {
'jest/globals': true,
},
globals: {
__DEV__: true,
window: true,
Expand All @@ -10,5 +15,5 @@ module.exports = {
rules: {
'prettier/prettier': 2,
indent: 'off',
}
},
};
1 change: 1 addition & 0 deletions ui/.github/workflows/build-test-release.yml
Expand Up @@ -104,6 +104,7 @@ jobs:

- run: yarn
- run: yarn run eslint
- run: yarn run test
- run: yarn run build
- run: |
yarn list --prod --depth 0 | python .github/scripts/format_yarn_deps.py --output-file=dist/package/appserver/static/js/dependencies.txt
Expand Down
13 changes: 13 additions & 0 deletions ui/babel.config.js
@@ -0,0 +1,13 @@
/* eslint no-undef: "error" */
/* eslint-env node */
module.exports = {
presets: [
[
'@babel/preset-react',
{
runtime: 'automatic',
},
],
'@babel/preset-env',
],
};
15 changes: 14 additions & 1 deletion ui/jest.config.js
@@ -1,3 +1,16 @@
/* eslint no-undef: "error" */
/* eslint-env node */
module.exports = {
testMatch: ['**/*.unit.[jt]s?(x)'],
// Mock
clearMocks: true,

// env settings
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
modulePathIgnorePatterns: ['<rootDir>/src/main/resources'],

// Coverage
collectCoverage: true,
collectCoverageFrom: ['src/main/webapp/**/*.{js,jsx}'],
coverageDirectory: 'coverage',
};
7 changes: 7 additions & 0 deletions ui/jest.setup.js
@@ -0,0 +1,7 @@
import '@testing-library/jest-dom';
import { configure } from '@testing-library/react';

/**
* Configure test attributes
*/
configure({ testIdAttribute: 'data-test' });
40 changes: 25 additions & 15 deletions ui/package.json
Expand Up @@ -10,52 +10,62 @@
"eslint": "eslint src --ext \".js,.jsx\"",
"eslint:ci": "yarn run eslint -f junit -o test-reports/lint-results.xml",
"eslint:fix": "eslint src --ext \".js, .jsx\" --fix",
"test": "jest --coverage",
"test:watch": "jest --watch",
"start": "webpack --watch",
"setup": "yarn && yarn run build",
"prepare": "husky install"
},
"dependencies": {
"@splunk/react-page": "^6.0.3",
"@splunk/react-toast-notifications": "^0.11.1",
"@splunk/react-ui": "^4.15.0",
"@splunk/splunk-utils": "^2.2.4",
"@splunk/themes": "^0.13.1",
"axios": "^1.2.1",
"@splunk/react-ui": "^4.16.0",
"@splunk/splunk-utils": "^2.3.0",
"@splunk/themes": "^0.14.0",
"axios": "^1.2.3",
"immutability-helper": "^3.1.1",
"jsonschema": "^1.4.0",
"license-webpack-plugin": "^4.0.2",
"react": "^16.9.38",
"react-dom": "^16.9.8",
"react-router-dom": "^6.6.0",
"react-router-dom": "^6.7.0",
"styled-components": "^5.3.6",
"uuid": "^9.0.0"
},
"devDependencies": {
"@babel/core": "^7.20.7",
"@babel/core": "^7.20.12",
"@babel/eslint-parser": "^7.19.1",
"@babel/plugin-transform-runtime": "^7.19.6",
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"@splunk/babel-preset": "^3.0.0",
"@splunk/eslint-config": "^4.0.0",
"@splunk/webpack-configs": "^6.0.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.2",
"babel-eslint": "^10.1.0",
"babel-loader": "^9.1.0",
"babel-jest": "^29.3.1",
"babel-loader": "^9.1.2",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3",
"css-loader": "^6.7.3",
"eslint": "^8.30.0",
"eslint": "^8.32.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.0",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.11",
"eslint-plugin-react": "^7.32.1",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.2",
"husky": "^8.0.3",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"lint-staged": "^13.1.0",
"prettier": "^2.8.1",
"prettier": "^2.8.3",
"semantic-release": "^19.0.5",
"style-loader": "^3.3.1",
"stylelint": "^14.16.0",
"stylelint": "^14.16.1",
"url": "^0.11.0",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
Expand Down
12 changes: 11 additions & 1 deletion ui/src/main/webapp/components/BaseFormView.jsx
Expand Up @@ -270,7 +270,11 @@ class BaseFormView extends PureComponent {
? this.currentInput[e.field]
: null;
tempEntity.value = e.encrypted ? '' : tempEntity.value;

/*
* Custom logic to handle edit/clone scenarios for the unencrypted file type field
* Reason: We are only getting file content in string format for API. and for validations object with file name, file size, and file content is expected.
*/
tempEntity.value = e.type === 'file' ? '' : tempEntity.value;
tempEntity.display =
typeof e?.options?.display !== 'undefined' ? e.options.display : true;
tempEntity.error = false;
Expand All @@ -284,6 +288,11 @@ class BaseFormView extends PureComponent {
} else if (props.mode === MODE_CLONE) {
tempEntity.value =
e.field === 'name' || e.encrypted ? '' : this.currentInput[e.field];
/*
* Custom logic to handle edit/clone scenarios for the unencrypted file type field
* Reason: We are only getting file content in string format for API. and for validations object with file name, file size, and file content is expected.
*/
tempEntity.value = e.type === 'file' ? '' : tempEntity.value;
tempEntity.display =
typeof e?.options?.display !== 'undefined' ? e.options.display : true;
tempEntity.error = false;
Expand Down Expand Up @@ -578,6 +587,7 @@ class BaseFormView extends PureComponent {
const body = new URLSearchParams();
Object.keys(this.datadict).forEach((key) => {
if (this.datadict[key] != null) {
// Custom logic for only sending file content in payload, not file name and file size.
if (
typeof this.datadict[key] === 'object' &&
this.entities.find((x) => x.field === key).type === 'file'
Expand Down
58 changes: 33 additions & 25 deletions ui/src/main/webapp/components/ConfigurationTable.jsx
@@ -1,4 +1,4 @@
import React, { useState, memo } from 'react';
import React, { useState, memo, useEffect } from 'react';
import PropTypes from 'prop-types';

import { TableContextProvider } from '../context/TableContext';
Expand All @@ -14,6 +14,12 @@ function ConfigurationTable({ selectedTab, updateIsPageOpen }) {

const isConfigurationPageStyle = selectedTab.style === STYLE_PAGE;

useEffect(() => {
if (isConfigurationPageStyle) {
updateIsPageOpen(!!entity.open);
}
}, [entity]); // eslint-disable-line react-hooks/exhaustive-deps

const handleRequestOpen = () => {
setEntity({
...entity,
Expand Down Expand Up @@ -55,39 +61,41 @@ function ConfigurationTable({ selectedTab, updateIsPageOpen }) {
// handle close request for page style dialog
const handlePageDialogClose = () => {
setEntity({ ...entity, open: false });
updateIsPageOpen(false);
};

// generate page style dialog
const generatePageDialog = () => {
updateIsPageOpen(true);
return (
<EntityPage
open={entity.open}
handleRequestClose={handlePageDialogClose}
serviceName={selectedTab.name}
stanzaName={entity.stanzaName}
mode={entity.mode}
formLabel={entity.formLabel}
page={PAGE_CONF}
/>
);
};

const getTableWrapper = () => (
<TableWrapper
page={PAGE_CONF}
const generatePageDialog = () => (
<EntityPage
open={entity.open}
handleRequestClose={handlePageDialogClose}
serviceName={selectedTab.name}
handleRequestModalOpen={() => handleRequestOpen()}
handleOpenPageStyleDialog={handleOpenPageStyleDialog}
stanzaName={entity.stanzaName}
mode={entity.mode}
formLabel={entity.formLabel}
page={PAGE_CONF}
/>
);

const getTableWrapper = () => (
<div
style={
isConfigurationPageStyle && entity.open ? { display: 'none' } : { display: 'block' }
}
>
<TableWrapper
page={PAGE_CONF}
serviceName={selectedTab.name}
handleRequestModalOpen={() => handleRequestOpen()}
handleOpenPageStyleDialog={handleOpenPageStyleDialog}
/>
</div>
);

return (
<TableContextProvider value={null}>
{isConfigurationPageStyle && entity.open ? generatePageDialog() : null}
{!(isConfigurationPageStyle && entity.open) ? getTableWrapper() : null}
{!isConfigurationPageStyle && entity.open ? generateModalDialog() : null}
{isConfigurationPageStyle && entity.open && generatePageDialog()}
{getTableWrapper()}
{!isConfigurationPageStyle && entity.open && generateModalDialog()}
</TableContextProvider>
);
}
Expand Down
51 changes: 51 additions & 0 deletions ui/src/main/webapp/components/CustomTab.jsx
@@ -0,0 +1,51 @@
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { _ } from '@splunk/ui-utils/i18n';
import { getUnifiedConfigs } from '../util/util';
import { getBuildDirPath } from '../util/script';

function CustomTab({ tab }) {
const [loading, setLoading] = useState(true);
const divRef = useRef(null);

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

const loadCustomTab = () =>
new Promise((resolve) => {
if (tab.customTab.type === 'external') {
import(
/* webpackIgnore: true */ `${getBuildDirPath()}/custom/${tab.customTab.src}.js`
).then((external) => {
const Control = external.default;
resolve(Control);
});
} else {
__non_webpack_require__(
[`app/${appName}/js/build/custom/${tab.customTab.src}`],
(Control) => resolve(Control)
);
}
});

useEffect(() => {
loadCustomTab().then((Control) => {
const customControl = new Control(tab, divRef.current);
customControl.render();
setLoading(false);
});
}, []); // eslint-disable-line react-hooks/exhaustive-deps

return (
<>
{loading && _('Loading...')}
<div ref={divRef} style={{ visibility: loading ? 'hidden' : 'visible' }} />
</>
);
}

CustomTab.propTypes = {
tab: PropTypes.object.isRequired,
};

export default CustomTab;
15 changes: 9 additions & 6 deletions ui/src/main/webapp/components/table/CustomTable.jsx
Expand Up @@ -33,11 +33,10 @@ function CustomTable({

const { rowData } = useContext(TableContext);

const { moreInfo, header } = tableConfig;
const headers = tableConfig.header;
const { moreInfo, header: headers, actions } = tableConfig;

const headerMapping = {};
header.forEach((x) => {
headers.forEach((x) => {
headerMapping[x.field] = x.mapping;
});

Expand Down Expand Up @@ -184,11 +183,14 @@ function CustomTable({
column.push({
...item,
sortKey: item.field || null,
isCustomMapping: !!item.mapping,
});
});
}
column.push({ label: 'Actions', field: 'actions', sortKey: '' });

if (actions && actions.length) {
column.push({ label: 'Actions', field: 'actions', sortKey: '' });
}

return column;
};

Expand All @@ -202,7 +204,7 @@ function CustomTable({
columns.map((headData) => (
<Table.HeadCell
key={headData.field}
onSort={(e) => (headData.sortKey ? handleSort(e, headData) : null)}
onSort={headData.sortKey ? handleSort : null}
sortKey={headData.sortKey ? headData.sortKey : null}
sortDir={
headData.sortKey && headData.sortKey === sortKey ? sortDir : 'none'
Expand All @@ -225,6 +227,7 @@ function CustomTable({
key={row.id}
row={row}
columns={columns}
rowActions={actions}
headerMapping={headerMapping}
{...{
handleEditActionClick,
Expand Down

0 comments on commit 0e5df40

Please sign in to comment.