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
Show file tree
Hide file tree
Changes from 7 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
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
import { QueryFormData } from '@superset-ui/core';
import { ControlPanelConfig } from 'packages/superset-ui-chart-controls/src/types';
import { Row } from './index';

export const defaultProps = {
export const defaultProps: Record<string, Partial<QueryFormData>> = {
origFormData: {
viz_type: 'altered_slice_tag_spec',
adhoc_filters: [
Expand Down Expand Up @@ -57,7 +60,7 @@ export const defaultProps = {
},
};

export const expectedDiffs = {
export const expectedDiffs: Record<string, Partial<QueryFormData>> = {
adhoc_filters: {
before: [
{
Expand Down Expand Up @@ -103,7 +106,7 @@ export const expectedDiffs = {
after: { x: 'y', z: 'z' },
},
};
export const expectedRows = [
export const expectedRows: Partial<Row>[] = [
{
control: 'Fake Filters',
before: 'a == hello',
Expand All @@ -128,7 +131,7 @@ export const expectedRows = [
after: '{"x":"y","z":"z"}',
},
];
export const fakePluginControls = {
export const fakePluginControls: ControlPanelConfig = {
controlPanelSections: [
{
label: 'Fake Control Panel Sections',
Expand Down
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: [];
}

export interface Row {
control: string;
before: string | number;
after: string | number;
}
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(): React.ReactNode {
const columns = [
{
accessor: 'control',
Expand Down Expand Up @@ -196,7 +227,7 @@ export default class AlteredSliceTag extends React.Component {
);
}

renderTriggerNode() {
renderTriggerNode(): React.ReactNode {
return (
<Tooltip id="difference-tooltip" title={t('Click to see difference')}>
<StyledLabel className="label">{t('Altered')}</StyledLabel>
Expand All @@ -223,4 +254,4 @@ export default class AlteredSliceTag extends React.Component {
}
}

AlteredSliceTag.propTypes = propTypes;
export default AlteredSliceTag;
Loading