Skip to content

Commit

Permalink
fix(table): custom mapping for values in Status column (#1451)
Browse files Browse the repository at this point in the history
**Issue number:**

## Summary

### Changes
Fix encountered issues during refactor:
- Correctly map status text when mapping declared in global config

Refactor Custom table row:
- Custom table row file moved to typescript
- add tests to cover table row file

### User experience

- Correctly sees mapped status text near toggle buttons

## Checklist

If your change doesn't seem to apply, please leave them unchecked.

* [x] I have performed a self-review of this change
* [x] Changes have been tested
* [ ] Changes are documented
* [x] PR title follows [conventional commit
semantics](https://www.conventionalcommits.org/en/v1.0.0/)

---------

Co-authored-by: srv-rr-github-token <[email protected]>
  • Loading branch information
soleksy-splunk and srv-rr-github-token authored Nov 13, 2024
1 parent f6dd96f commit 4721738
Show file tree
Hide file tree
Showing 19 changed files with 360 additions and 51 deletions.
2 changes: 1 addition & 1 deletion ui/src/components/BaseFormView/BaseFormView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
ChangeRecord,
CustomHookClass,
EntitiesAllowingModifications,
} from './BaseFormTypes';
} from '../../types/components/BaseFormTypes';
import {
getAllFieldsWithModifications,
getModifiedState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { setUnifiedConfig } from '../../../util/util';
import { GlobalConfig } from '../../../types/globalConfig/globalConfig';
import { Mode } from '../../../constants/modes';
import { BaseFormProps } from '../BaseFormTypes';
import { BaseFormProps } from '../../../types/components/BaseFormTypes';
import { Platforms } from '../../../types/globalConfig/pages';
import {
getGlobalConfigMockGroupsFoInputPage,
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/ControlWrapper/ControlWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ControlGroup from '@splunk/react-ui/ControlGroup';
import styled from 'styled-components';
import MarkdownMessage from '../MarkdownMessage/MarkdownMessage';
import CONTROL_TYPE_MAP, { ComponentTypes } from '../../constants/ControlTypeMap';
import { AnyEntity, UtilControlWrapper } from '../BaseFormView/BaseFormTypes';
import { AnyEntity, UtilControlWrapper } from '../../types/components/BaseFormTypes';
import { AcceptableFormValueOrNullish } from '../../types/components/shareableTypes';
import CustomControl from '../CustomControl/CustomControl';
import { Mode } from '../../constants/modes';
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/CustomControl/CustomControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { _ } from '@splunk/ui-utils/i18n';
import { getUnifiedConfigs } from '../../util/util';
import { getBuildDirPath } from '../../util/script';
import { AcceptableFormValueOrNullish } from '../../types/components/shareableTypes';
import { UtilBaseForm } from '../BaseFormView/BaseFormTypes';
import { UtilBaseForm } from '../../types/components/BaseFormTypes';
import { GlobalConfig } from '../../types/globalConfig/globalConfig';
import { Mode } from '../../constants/modes';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Mode } from '../../constants/modes';
import { AcceptableFormValueOrNullish } from '../../types/components/shareableTypes';
import { GlobalConfig } from '../../types/globalConfig/globalConfig';
import { UtilControlWrapper } from '../BaseFormView/BaseFormTypes';
import { UtilControlWrapper } from '../../types/components/BaseFormTypes';

export class CustomControlMockForTest {
globalConfig: GlobalConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
thirdModificationField,
} from './TestConfig';
import EntityModal, { EntityModalProps } from '../EntityModal/EntityModal';
import { EntitiesAllowingModifications } from '../BaseFormView/BaseFormTypes';
import { EntitiesAllowingModifications } from '../../types/components/BaseFormTypes';
import { invariant } from '../../util/invariant';

const handleRequestClose = jest.fn();
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/FormModifications/FormModifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
BaseFormState,
AnyEntity,
EntitiesAllowingModifications,
} from '../BaseFormView/BaseFormTypes';
} from '../../types/components/BaseFormTypes';
import { MarkdownMessageProps } from '../MarkdownMessage/MarkdownMessage';

const VALUE_TO_TRIGGER_UPDATE_FOR_ANY_NOT_LISTED_VALUES = '[[any_other_value]]';
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/table/CustomTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const CustomTable: React.FC<CustomTableProps> = ({
: undefined;
const { moreInfo, header: headers, actions } = tableConfig;

const headerMapping: Record<string, unknown> = {};
const headerMapping: Record<string, Record<string, string> | undefined> = {};

headers.forEach((x) => {
headerMapping[x.field] = x.mapping;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import React, { ReactElement, useCallback } from 'react';

import WaitSpinner from '@splunk/react-ui/WaitSpinner';
import Switch from '@splunk/react-ui/Switch';
Expand All @@ -16,6 +15,7 @@ import { _ } from '@splunk/ui-utils/i18n';
import CustomTableControl from './CustomTableControl';
import { ActionButtonComponent } from './CustomTableStyle';
import { getTableCellValue } from './table.utils';
import { RowDataFields } from '../../context/TableContext';

const TableCellWrapper = styled(Table.Cell)`
padding: 2px;
Expand All @@ -30,7 +30,24 @@ const SwitchWrapper = styled.div`
}
`;

function CustomTableRow(props) {
interface CustomTableRowProps {
row: RowDataFields;
readonly?: boolean;
columns: Array<{ customCell?: { src?: string; type?: string }; field: string }>;
rowActions: string[];
headerMapping: Record<string, Record<string, string> | undefined>;
handleToggleActionClick: (row: RowDataFields) => void;
handleEditActionClick: (row: RowDataFields) => void;
handleCloneActionClick: (row: RowDataFields) => void;
handleDeleteActionClick: (row: RowDataFields) => void;
}

interface CellHeader {
field: string;
customCell?: { src?: string; type?: string };
}

function CustomTableRow(props: CustomTableRowProps) {
const {
row,
columns,
Expand All @@ -42,7 +59,9 @@ function CustomTableRow(props) {
handleDeleteActionClick,
} = props;

const getCustomCell = (customRow, header) =>
const getCustomCell = (customRow: RowDataFields, header: CellHeader) =>
header.customCell?.src &&
header.customCell?.type &&
React.createElement(CustomTableControl, {
serviceName: row.serviceName,
field: header.field,
Expand All @@ -52,15 +71,15 @@ function CustomTableRow(props) {
});

const rowActionsPrimaryButton = useCallback(
(selectedRow, header) => (
(selectedRow: RowDataFields, header: CellHeader) => (
<TableCellWrapper data-column="actions" key={header.field}>
<ButtonGroup>
{!props.readonly && rowActions.includes('edit') && (
<Tooltip content={_('Edit')}>
<ActionButtonComponent
appearance="flat"
aria-label={_('Edit')}
icon={<Pencil screenReaderText={null} size={1} />}
icon={<Pencil />}
onClick={() => handleEditActionClick(selectedRow)}
className="editBtn"
/>
Expand All @@ -71,7 +90,7 @@ function CustomTableRow(props) {
<ActionButtonComponent
appearance="flat"
aria-label={_('Clone')}
icon={<Clone screenReaderText={null} size={1} />}
icon={<Clone size={1} />}
onClick={() => handleCloneActionClick(selectedRow)}
className="cloneBtn"
/>
Expand All @@ -84,8 +103,11 @@ function CustomTableRow(props) {
)}
>
<ActionButtonComponent
aria-label={_(
`Go to search for events associated with ${selectedRow.name}`
)}
appearance="flat"
icon={<Magnifier screenReaderText={null} size={1} />}
icon={<Magnifier />}
to={`/app/search/search?q=search%20index%3D_internal%20source%3D*${selectedRow.name}*`}
className="searchBtn"
inline={false}
Expand All @@ -98,7 +120,7 @@ function CustomTableRow(props) {
<ActionButtonComponent
appearance="flat"
aria-label={_('Delete')}
icon={<Trash screenReaderText={null} size={1} />}
icon={<Trash size={1} />}
onClick={() => handleDeleteActionClick(selectedRow)}
className="deleteBtn"
/>
Expand All @@ -111,31 +133,29 @@ function CustomTableRow(props) {
[handleEditActionClick, handleCloneActionClick, handleDeleteActionClick]
);

let statusContent = 'Active';
let statusContent: string | ReactElement = row.disabled ? 'Inactive' : 'Active';
// eslint-disable-next-line no-underscore-dangle
if (row.__toggleShowSpinner) {
statusContent = <WaitSpinner />;
} else if (row.disabled) {
statusContent =
headerMapping?.disabled && headerMapping.disabled[row.disabled]
? headerMapping.disabled[row.disabled]
: 'Inactive';
} else if (headerMapping.disabled?.[String(row.disabled)]) {
statusContent = headerMapping.disabled[String(row.disabled)];
}

// Fix set of props are passed to Table.Row element
return (
<Table.Row // nosemgrep: typescript.react.security.audit.react-props-injection.react-props-injection, typescript.react.best-practice.react-props-spreading.react-props-spreading
key={row.name || row.id}
{...props}
aria-label={`row-${row.name || row.id}`}
>
{columns &&
columns.length &&
columns.map((header) => {
let cellHTML = '';
let cellHTML: string | ReactElement = '';
if (header.customCell && header.customCell.src) {
cellHTML = (
<Table.Cell data-column={header.field} key={header.field}>
{getCustomCell(row, header)}
{header.customCell && getCustomCell(row, header)}
</Table.Cell>
);
} else if (header.field === 'disabled') {
Expand All @@ -147,8 +167,10 @@ function CustomTableRow(props) {
value={row.disabled}
onClick={() => handleToggleActionClick(row)}
selected={!row.disabled}
// eslint-disable-next-line no-underscore-dangle
disabled={row.__toggleShowSpinner || props.readonly}
disabled={
// eslint-disable-next-line no-underscore-dangle
Boolean(row.__toggleShowSpinner) || props.readonly
}
appearance="toggle"
className="toggle_switch"
selectedLabel={_(
Expand Down Expand Up @@ -185,16 +207,4 @@ function CustomTableRow(props) {
);
}

CustomTableRow.propTypes = {
row: PropTypes.any,
readonly: PropTypes.bool,
columns: PropTypes.array,
rowActions: PropTypes.array,
headerMapping: PropTypes.object,
handleToggleActionClick: PropTypes.func,
handleEditActionClick: PropTypes.func,
handleCloneActionClick: PropTypes.func,
handleDeleteActionClick: PropTypes.func,
};

export default React.memo(CustomTableRow);
38 changes: 37 additions & 1 deletion ui/src/components/table/stories/TableWrapper.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { fn } from '@storybook/test';
import { setUnifiedConfig } from '../../../util/util';
import { GlobalConfig } from '../../../types/globalConfig/globalConfig';
import TableWrapper, { ITableWrapperProps } from '../TableWrapper';
import { getSimpleConfig } from './configMockups';
import {
getSimpleConfig,
getSimpleConfigStylePage,
getSimpleConfigWithMapping,
} from './configMockups';
import { TableContextProvider } from '../../../context/TableContext';
import { ServerHandlers } from './rowDataMockup';

Expand Down Expand Up @@ -43,3 +47,35 @@ export const OuathBasic: Story = {
},
},
};

export const SimpleTableStylePage: Story = {
args: {
page: 'inputs',
serviceName: 'example_input_one',
handleRequestModalOpen: fn(),
handleOpenPageStyleDialog: fn(),
displayActionBtnAllRows: false,
config: getSimpleConfigStylePage() as GlobalConfig,
},
parameters: {
msw: {
handlers: ServerHandlers,
},
},
};

export const SimpleConfigWithStatusMapped: Story = {
args: {
page: 'configuration',
serviceName: 'account',
handleRequestModalOpen: fn(),
handleOpenPageStyleDialog: fn(),
displayActionBtnAllRows: false,
config: getSimpleConfigWithMapping() as GlobalConfig,
},
parameters: {
msw: {
handlers: ServerHandlers,
},
},
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 4721738

Please sign in to comment.