Skip to content
This repository was archived by the owner on Jan 16, 2025. It is now read-only.

Commit

Permalink
possible fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrea Rosci authored and Andrea Rosci committed Nov 15, 2024
1 parent d000cb2 commit f0f2401
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 117 deletions.
47 changes: 24 additions & 23 deletions src/AutoUI/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,14 @@ import {
useAnalyticsContext,
} from '@balena/ui-shared-components';
import type { FiltersView } from '../components/Filters';
import { ajvFilter } from '../components/Filters/SchemaSieve';
import {
ajvFilter,
convertFilterToHumanReadable,
} from '../components/Filters/SchemaSieve';
import type { Format } from '../components/Widget/utils';
import type { Dictionary } from '../common';
import { defaultFormats } from '../components/Widget/Formats';
import { listFilterQuery } from './Filters/PersistentFilters';
import { removeRefSchemeSeparatorsFromFilters } from './Filters/utils';

const { Box, styled } = Material;

const HeaderGrid = styled(Box)(({ theme }) => ({
Expand Down Expand Up @@ -183,8 +185,16 @@ export const AutoUI = <T extends AutoUIBaseResource<T>>({
const history = useHistory();
// Use a flag to make sure table view event is only triggered once (without the tag
// it will be triggered whenever the data is updated)
const [shouldTableViewEventBeTriggered, setShouldTableViewEventBeTriggered] =
React.useState(true);
const shouldTableViewEventBeTriggered = React.useRef(true);
const totalItems = React.useMemo(
() =>
pagination && 'totalItems' in pagination
? pagination.totalItems
: Array.isArray(data)
? data.length
: null,
[pagination, data],
);

const modelRef = React.useRef(modelRaw);
// This allows the component to work even if
Expand All @@ -198,10 +208,7 @@ export const AutoUI = <T extends AutoUIBaseResource<T>>({

const [filters, setFilters] = React.useState<JSONSchema[]>([]);
const [sort, setSort] = React.useState<TableSortOptions<T> | null>(
() =>
(getFromLocalStorage(`${model.resource}__sort`) as
| TableSortOptions<T>
| undefined) || null,
() => getFromLocalStorage(`${model.resource}__sort`) || null,
);
const [internalPagination, setInternalPagination] = React.useState<{
page: number;
Expand Down Expand Up @@ -438,28 +445,22 @@ export const AutoUI = <T extends AutoUIBaseResource<T>>({
};

React.useEffect(() => {
if (!lens || !shouldTableViewEventBeTriggered) {
if (!lens || !shouldTableViewEventBeTriggered || !filters.length) {
return;
}

const dataCount = Array.isArray(data) ? data.length : null;
const totalItems = pagination?.serverSide
? pagination.totalItems
: dataCount;
const amplitudeFilter = filters.map((f) => convertFilterToHumanReadable(f));

analytics.webTracker?.track('Resource List View', {
lens: lens.slug,
resource: model.resource,
totalItems,
filters: Object.assign(
{},
listFilterQuery(removeRefSchemeSeparatorsFromFilters(filters), false),
),
filters: amplitudeFilter,
sort,
});

setShouldTableViewEventBeTriggered(false);
}, [lens, model.resource, pagination, filters, sort, data]);
shouldTableViewEventBeTriggered.current = false;
}, [lens, model.resource, filters, sort, totalItems]);

if (loading && data == null) {
return (
Expand Down Expand Up @@ -551,8 +552,6 @@ export const AutoUI = <T extends AutoUIBaseResource<T>>({
resource: model.resource,
lens: lens.slug,
});

setShouldTableViewEventBeTriggered(true);
}}
/>
</HeaderGrid>
Expand Down Expand Up @@ -643,7 +642,9 @@ export const AutoUI = <T extends AutoUIBaseResource<T>>({
{actionData?.action?.renderer?.({
schema: actionData.schema,
affectedEntries: actionData.affectedEntries,
onDone: () => setActionData(undefined),
onDone: () => {
setActionData(undefined);
},
setSelected: $setSelected,
})}
</Box>
Expand Down
99 changes: 7 additions & 92 deletions src/components/Filters/FilterDescription.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,8 @@
import type { JSONSchema7 as JSONSchema } from 'json-schema';
import * as React from 'react';
import { Tag, type TagProps, TagItem } from 'rendition';
import {
FULL_TEXT_SLUG,
parseFilterDescription,
type FilterDescription as SieveFilterDescription,
} from './SchemaSieve';
import { isDateTimeFormat } from '../../DataTypes';
import { format as dateFormat } from 'date-fns';
import { isJSONSchema } from '../../AutoUI/schemaOps';
import isEqual from 'lodash/isEqual';
import { findInObject } from '../../AutoUI/utils';

const transformToReadableValue = (
parsedFilterDescription: SieveFilterDescription,
): string => {
const { schema, value } = parsedFilterDescription;
if (schema && isDateTimeFormat(schema.format)) {
return dateFormat(value, 'PPPppp');
}
const schemaEnum: JSONSchema['enum'] = findInObject(schema, 'enum');
const schemaEnumNames: string[] | undefined = findInObject(
schema,
'enumNames',
);
if (schemaEnum && schemaEnumNames) {
const index = schemaEnum.findIndex((a) => isEqual(a, value));
return (schemaEnumNames as string[])[index];
}

const oneOf: JSONSchema['oneOf'] = findInObject(schema, 'oneOf');
if (oneOf) {
const selected = oneOf.find(
(o) => isJSONSchema(o) && isEqual(o.const, value),
);

return isJSONSchema(selected) && selected.title ? selected.title : value;
}

if (typeof value === 'object') {
if (Object.keys(value).length > 1) {
return Object.entries(value)
.map(([key, value]) => {
const property = schema.properties?.[key];
return isJSONSchema(property)
? `${property.title ?? key}: ${value}`
: `${key}: ${value}`;
})
.join(', ');
}
return Object.values(value)[0] as string;
}

return String(value);
};
import type { TagItem } from 'rendition';
import { Tag, type TagProps } from 'rendition';
import { convertFilterToHumanReadable } from './SchemaSieve';

export interface FilterDescriptionProps extends Omit<TagProps, 'value'> {
filter: JSONSchema;
Expand All @@ -63,44 +12,10 @@ export const FilterDescription = ({
filter,
...props
}: FilterDescriptionProps) => {
const tagProps = React.useMemo(() => {
if (filter.title === FULL_TEXT_SLUG) {
const parsedFilterDescription = parseFilterDescription(filter);
if (!parsedFilterDescription) {
return;
}
return parsedFilterDescription
? [
{
name: parsedFilterDescription.field,
operator: 'contains',
value: transformToReadableValue(parsedFilterDescription),
},
]
: undefined;
}

return filter.anyOf
?.map<TagItem | undefined>((f, index) => {
if (!isJSONSchema(f)) {
return;
}
const parsedFilterDescription = parseFilterDescription(f);
if (!parsedFilterDescription) {
return;
}
const value = transformToReadableValue(parsedFilterDescription);
return {
name:
parsedFilterDescription?.schema?.title ??
parsedFilterDescription.field,
operator: parsedFilterDescription.operator,
value,
prefix: index > 0 ? 'or' : undefined,
};
})
.filter((f): f is TagItem => Boolean(f));
}, [filter]);
const tagProps = React.useMemo(
() => convertFilterToHumanReadable(filter) as TagItem[],
[filter],
);

return tagProps ? <Tag mt={2} multiple={tagProps} {...props} /> : null;
};
89 changes: 87 additions & 2 deletions src/components/Filters/SchemaSieve.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {
import type {
JSONSchema7 as JSONSchema,
JSONSchema7Definition as JSONSchemaDefinition,
} from 'json-schema';
Expand All @@ -12,6 +12,9 @@ import ajvKeywords from 'ajv-keywords';
import addFormats from 'ajv-formats';
import pickBy from 'lodash/pickBy';
import { enqueueSnackbar } from '@balena/ui-shared-components';
import { format as dateFormat } from 'date-fns';
import isEqual from 'lodash/isEqual';
import { findInObject } from '../../AutoUI/utils';

const ajv = new Ajv();
// TODO: remove the any cast as soon as we remove rendition
Expand Down Expand Up @@ -120,7 +123,7 @@ export const createFilter = (
if (!field || !operator) {
return {};
}
const propertySchema = properties[field] as JSONSchema;
const propertySchema = properties[field];
const operators = getAllOperators(propertySchema);

Check failure on line 127 in src/components/Filters/SchemaSieve.ts

View workflow job for this annotation

GitHub Actions / Flowzone / Test npm (20.x)

Argument of type 'JSONSchema7Definition' is not assignable to parameter of type 'JSONSchema7'.
const operatorLabel = operators[operator as keyof typeof operators];
const filter = createModelFilter(propertySchema, {
Expand Down Expand Up @@ -253,3 +256,85 @@ export const parseFilterDescription = (
return;
}
};

const transformToReadableValue = (
parsedFilterDescription: FilterDescription,
): string => {
const { schema, value } = parsedFilterDescription;
if (schema && isDateTimeFormat(schema.format)) {
return dateFormat(value, 'PPPppp');
}
const schemaEnum: JSONSchema['enum'] = findInObject(schema, 'enum');
const schemaEnumNames: string[] | undefined = findInObject(
schema,
'enumNames',
);
if (schemaEnum && schemaEnumNames) {
const index = schemaEnum.findIndex((a) => isEqual(a, value));
return schemaEnumNames[index];
}

const oneOf: JSONSchema['oneOf'] = findInObject(schema, 'oneOf');
if (oneOf) {
const selected = oneOf.find(
(o) => isJSONSchema(o) && isEqual(o.const, value),
);

return isJSONSchema(selected) && selected.title ? selected.title : value;
}

if (typeof value === 'object') {
if (Object.keys(value).length > 1) {
return Object.entries(value)
.map(([key, value]) => {
const property = schema.properties?.[key];
return isJSONSchema(property)
? `${property.title ?? key}: ${value}`
: `${key}: ${value}`;
})
.join(', ');
}
return Object.values(value)[0] as string;
}

return String(value);
};

export const convertFilterToHumanReadable = (filter: JSONSchema) => {
if (filter.title === FULL_TEXT_SLUG) {
const parsedFilterDescription = parseFilterDescription(filter);
if (!parsedFilterDescription) {
return;
}
return parsedFilterDescription
? [
{
name: parsedFilterDescription.field,
operator: 'contains',
value: transformToReadableValue(parsedFilterDescription),
},
]
: undefined;
}

return filter.anyOf
?.map((f, index) => {
if (!isJSONSchema(f)) {
return;
}
const parsedFilterDescription = parseFilterDescription(f);
if (!parsedFilterDescription) {
return;
}
const value = transformToReadableValue(parsedFilterDescription);
return {
name:
parsedFilterDescription?.schema?.title ??
parsedFilterDescription.field,
operator: parsedFilterDescription.operator,
value,
prefix: index > 0 ? 'or' : undefined,
};
})
.filter((f) => Boolean(f));
};

0 comments on commit f0f2401

Please sign in to comment.