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

chore: Migrate AlteredSliceTag to typescript #27030

Merged
merged 14 commits into from
Feb 13, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -17,63 +17,92 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { isEqual, isEmpty } from 'lodash';
import { styled, t } from '@superset-ui/core';
import { QueryFormData, styled, t } from '@superset-ui/core';
import { sanitizeFormData } from 'src/explore/exploreUtils/formData';
import getControlsForVizType from 'src/utils/getControlsForVizType';
import { safeStringify } from 'src/utils/safeStringify';
import { Tooltip } from 'src/components/Tooltip';
import ModalTrigger from '../ModalTrigger';
import TableView from '../TableView';

const propTypes = {
origFormData: PropTypes.object.isRequired,
currentFormData: PropTypes.object.isRequired,
};
interface AlteredSliceTagProps {
origFormData: QueryFormData;
currentFormData: QueryFormData;
}

export interface ControlMap {
EnxDev marked this conversation as resolved.
Show resolved Hide resolved
[key: string]: {
label?: string;
type?: string;
};
}

interface Diff {
before: [];
after: [];
}

interface Row {
control: string;
before: string;
after: string;
}
interface FilterItem {
EnxDev marked this conversation as resolved.
Show resolved Hide resolved
comparator?: string | string[];
subject: string;
operator: string;
label?: string;
}
interface AlteredSliceTagState {
rows: Row[];
hasDiffs: boolean;
controlsMap: ControlMap;
}

const StyledLabel = styled.span`
${({ theme }) => `
font-size: ${theme.typography.sizes.s}px;
color: ${theme.colors.grayscale.dark1};
background-color: ${theme.colors.alert.base};

&: hover {
&:hover {
background-color: ${theme.colors.alert.dark1};
}
`}
`;

function alterForComparison(value) {
// Considering `[]`, `{}`, `null` and `undefined` as identical
// for this purpose
function alterForComparison(value: string): [] | Object | null {
// Treat `null`, `undefined`, and empty strings as equivalent
if (value === undefined || value === null || value === '') {
return null;
}
if (typeof value === 'object') {
if (Array.isArray(value) && value.length === 0) {
return null;
}
const keys = Object.keys(value);
if (keys && keys.length === 0) {
return null;
}
// Treat empty arrays and objects as equivalent to null
if (Array.isArray(value) && value.length === 0) {
return null;
}
if (typeof value === 'object' && Object.keys(value).length === 0) {
return null;
}
return value;
}

export default class AlteredSliceTag extends React.Component {
constructor(props) {
class AlteredSliceTag extends React.Component<
AlteredSliceTagProps,
AlteredSliceTagState
> {
constructor(props: AlteredSliceTagProps) {
super(props);
const diffs = this.getDiffs(props);
const controlsMap = getControlsForVizType(this.props.origFormData.viz_type);
const controlsMap: ControlMap = getControlsForVizType(
props.origFormData.viz_type,
) as ControlMap;
const rows = this.getRowsFromDiffs(diffs, controlsMap);

this.state = { rows, hasDiffs: !isEmpty(diffs), controlsMap };
}

UNSAFE_componentWillReceiveProps(newProps) {
// Update differences if need be
UNSAFE_componentWillReceiveProps(newProps: AlteredSliceTagProps): void {
if (isEqual(this.props, newProps)) {
return;
}
Expand All @@ -84,22 +113,22 @@ export default class AlteredSliceTag extends React.Component {
}));
}

getRowsFromDiffs(diffs, controlsMap) {
getRowsFromDiffs(
diffs: { [key: string]: Diff },
controlsMap: ControlMap,
): Row[] {
return Object.entries(diffs).map(([key, diff]) => ({
control: (controlsMap[key] && controlsMap[key].label) || key,
control: controlsMap[key]?.label || key,
before: this.formatValue(diff.before, key, controlsMap),
after: this.formatValue(diff.after, key, controlsMap),
}));
}

getDiffs(props) {
// Returns all properties that differ in the
// current form data and the saved form data
getDiffs(props: AlteredSliceTagProps): { [key: string]: Diff } {
const ofd = sanitizeFormData(props.origFormData);
const cfd = sanitizeFormData(props.currentFormData);

const fdKeys = Object.keys(cfd);
const diffs = {};
const diffs: { [key: string]: Diff } = {};
fdKeys.forEach(fdKey => {
if (!ofd[fdKey] && !cfd[fdKey]) {
return;
Expand All @@ -114,13 +143,15 @@ export default class AlteredSliceTag extends React.Component {
return diffs;
}

isEqualish(val1, val2) {
isEqualish(val1: string, val2: string): boolean {
return isEqual(alterForComparison(val1), alterForComparison(val2));
}

formatValue(value, key, controlsMap) {
// Format display value based on the control type
// or the value type
formatValue(
value: FilterItem[],
key: string,
controlsMap: ControlMap,
): string {
if (value === undefined) {
return 'N/A';
}
Expand Down Expand Up @@ -167,7 +198,7 @@ export default class AlteredSliceTag extends React.Component {
return safeStringify(value);
}

renderModalBody() {
renderModalBody(): JSX.Element {
EnxDev marked this conversation as resolved.
Show resolved Hide resolved
const columns = [
{
accessor: 'control',
Expand Down Expand Up @@ -196,15 +227,15 @@ export default class AlteredSliceTag extends React.Component {
);
}

renderTriggerNode() {
renderTriggerNode(): JSX.Element {
EnxDev marked this conversation as resolved.
Show resolved Hide resolved
return (
<Tooltip id="difference-tooltip" title={t('Click to see difference')}>
<StyledLabel className="label">{t('Altered')}</StyledLabel>
</Tooltip>
);
}

render() {
render(): JSX.Element | null {
EnxDev marked this conversation as resolved.
Show resolved Hide resolved
// Return nothing if there are no differences
if (!this.state.hasDiffs) {
return null;
Expand All @@ -223,4 +254,4 @@ export default class AlteredSliceTag extends React.Component {
}
}

AlteredSliceTag.propTypes = propTypes;
export default AlteredSliceTag;