Skip to content

Commit

Permalink
Release 5.8.6
Browse files Browse the repository at this point in the history
### Changelog:
* Feature(frontend): Add to container CSS classes from fields inside NestedObjectField.
* Fix(frontend): Remove empty css classes generated for fields values.
  • Loading branch information
onegreyonewhite committed Oct 27, 2023
2 parents 28c8d82 + 216b755 commit bcd58eb
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { expect, test } from '@jest/globals';
import { createApp, createSchema } from '@/unittests';
import { useEntityViewClasses } from '@/vstutils/store';
import { ref } from 'vue';
import { emptyRepresentData } from '@/vstutils/utils';

test('fields container classes', async () => {
const app = await createApp({ schema: createSchema() });

const TestModel = app.modelsResolver.bySchemaObject({
properties: {
some_boolean: { type: 'boolean' },
some_choice: { type: 'string', enum: ['option1', 'option2'] },
some_string: { type: 'string' },
some_object: {
type: 'object',
properties: {
some_boolean: { type: 'boolean' },
some_choice: { type: 'string', enum: ['option1', 'option2'] },
some_string: { type: 'string' },
},
},
},
});

const data = ref(emptyRepresentData());
const classes = useEntityViewClasses(ref(TestModel), data);

expect(classes.value).toStrictEqual([]);

data.value = {
...emptyRepresentData(),
some_boolean: true,
some_choice: 'option1',
some_string: 'some str',
some_object: {
some_boolean: false,
some_choice: 'option2',
some_string: 'other str',
},
};

expect(classes.value).toStrictEqual([
'field-some_boolean-true',
'field-some_choice-option1',
'field-some_object-some_boolean-false',
'field-some_object-some_choice-option2',
]);
});
15 changes: 14 additions & 1 deletion frontend_src/vstutils/fields/base/BaseField.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Schema, ParameterType, ParameterCollectionFormat } from 'swagger-schema-official';
import { defineComponent, markRaw, toRaw } from 'vue';
import type { InnerData, RepresentData } from '../../utils';
import { _translate, capitalize, deepEqual, nameToTitle, X_OPTIONS } from '../../utils';
import { _translate, capitalize, deepEqual, nameToTitle, X_OPTIONS, stringToCssClass } from '../../utils';
import { pop_up_msg } from '../../popUp';
import type { Model, ModelConstructor } from '../../models';
import BaseFieldMixin from './BaseFieldMixin.vue';
Expand Down Expand Up @@ -116,6 +116,8 @@ export interface Field<
isSameValues(data1: RepresentData, data2: RepresentData): boolean;

parseFieldError(errorData: unknown, instanceData: InnerData): unknown;

getContainerCssClasses(data: RepresentData): string[] | undefined;
}

export type FieldMixin = ComponentOptionsMixin | ComponentOptions<Vue> | typeof Vue;
Expand Down Expand Up @@ -408,6 +410,17 @@ export class BaseField<Inner, Represent, XOptions extends DefaultXOptions = Defa
}
return errorData as Record<string, unknown>;
}

protected formatContainerCssClass(value: string | null | undefined) {
if (value === undefined) {
return `field-${this.name}`;
}
return `field-${this.name}-${stringToCssClass(value)}`;
}

getContainerCssClasses(_data: RepresentData): string[] | undefined {
return undefined;
}
}

export type FieldConstructor = typeof BaseField;
Expand Down
7 changes: 7 additions & 0 deletions frontend_src/vstutils/fields/boolean/BooleanField.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ class BooleanField extends BaseField {
static get mixins() {
return [BooleanFieldMixin];
}

getContainerCssClasses(data) {
const value = this.getValue(data);
if (value !== undefined) {
return [this.formatContainerCssClass(String(value))];
}
}
}

export default BooleanField;
9 changes: 9 additions & 0 deletions frontend_src/vstutils/fields/choices/ChoicesField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { i18n } from '@/vstutils/translation';
import type { FieldOptions, FieldXOptions } from '../base';
import { StringField } from '../text';
import ChoicesFieldMixin from './ChoicesFieldMixin.js';
import type { RepresentData } from '@/vstutils/utils';

export type RawEnumItem = string | [string, string] | { value: string; prefetch_value: string } | Model;

Expand Down Expand Up @@ -96,4 +97,12 @@ export class ChoicesField extends StringField<ChoicesFieldXOptions> {
static get mixins() {
return [ChoicesFieldMixin];
}

getContainerCssClasses(data: RepresentData) {
const value = this.getValue(data);
if (value) {
return [this.formatContainerCssClass(value)];
}
return [];
}
}
15 changes: 15 additions & 0 deletions frontend_src/vstutils/fields/nested-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,19 @@ export class NestedObjectField
}
return validatedValue;
}

getContainerCssClasses(data: RepresentData) {
const value = this.getValue(data);
const classes = [];
for (const field of this.nestedModel!.fields.values()) {
const fieldClasses = field.getContainerCssClasses(value ?? emptyRepresentData());
if (fieldClasses) {
const prefix = `field-${this.name}-`;
for (const fieldClass of fieldClasses) {
classes.push(fieldClass.replace(/^field-/, prefix));
}
}
}
return classes;
}
}
4 changes: 2 additions & 2 deletions frontend_src/vstutils/store/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { filterOperations, signals } from '@/vstutils/signals';
import { i18n } from '@/vstutils/translation';
import { useRoute } from 'vue-router/composables';
import {
classesFromFields,
getApp,
getRedirectUrlFromResponse,
IGNORED_FILTERS,
Expand All @@ -33,6 +32,7 @@ import {
pathToArray,
emptyRepresentData,
emptyInnerData,
classesFromFields,
} from '@/vstutils/utils';

import type { Ref } from 'vue';
Expand Down Expand Up @@ -126,7 +126,7 @@ export const useQuerySet = (view: IView) => {
return { queryset, setQuerySet };
};

export const useEntityViewClasses = (modelClass: Ref<ModelConstructor>, data: Ref<Record<string, any>>) => {
export const useEntityViewClasses = (modelClass: Ref<ModelConstructor>, data: Ref<RepresentData>) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return
const flatFields = computed(() => Array.from(modelClass.value.fields.values()));
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
Expand Down
29 changes: 20 additions & 9 deletions frontend_src/vstutils/utils/todo.js
Original file line number Diff line number Diff line change
Expand Up @@ -906,17 +906,24 @@ export function generateBase32String(length = 32) {
return generateRandomString(length, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567');
}

/**
* @param {Iterable<import('@/vstutils/fields/base').Field>} fields
* @param {RepresentData} data
* @return {string[]}
*/
export function classesFromFields(fields, data) {
return fields
.filter((f) => f.format === 'choices' || f.type === 'boolean')
.map((f) => {
let cls = `field-${f.name}`;
const value = f._getValueFromData(data);
if (value !== undefined) {
cls += `-${stringToCssClass(value)}`;
const classes = new Set();

for (const field of fields) {
const classNames = field.getContainerCssClasses(data);
if (classNames) {
for (const cls of classNames) {
classes.add(cls);
}
return cls;
});
}
}

return Array.from(classes);
}

export function parseResponseMessage(data) {
Expand Down Expand Up @@ -1007,6 +1014,10 @@ export function chunkArray(array, chunkSize) {
}, []);
}

/**
* @param {unknown} str
* @returns {string}
*/
export function stringToCssClass(str) {
if (typeof str !== 'string') str = String(str);
return str.replace(/\s/g, '');
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@
"jquery.scrollto/jquery": "3.7.1",
"admin-lte/jquery-validation": "^1.19.5",
"admin-lte/jszip": "^3.10.1",
"admin-lte/**/jquery": "3.7.1"
"admin-lte/**/jquery": "3.7.1",
"admin-lte/sweetalert2": "10.16.9"
},
"devDependencies": {
"@babel/core": "^7.16.7",
Expand Down
2 changes: 1 addition & 1 deletion vstutils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# pylint: disable=django-not-available
__version__: str = '5.8.5'
__version__: str = '5.8.6'
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7229,10 +7229,10 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==

sweetalert2@^10.16.9:
version "10.16.11"
resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-10.16.11.tgz#c8604059df2f31e06df56b02bd12b2f07ef7f098"
integrity sha512-Rdfabv2G89Tr8vmUTb1auWCYYesKBEWnkYPSi7XaiCIW0ZXXGK8Nw1wYKPEMLU6O8gMSMJe5m6MRKqMQsAQy9A==
sweetalert2@10.16.9, sweetalert2@^10.16.9:
version "10.16.9"
resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-10.16.9.tgz#8ed86f2fa811a136667a48357e204348705be8c9"
integrity sha512-oNe+md5tmmS3fGfVHa7gVPlun7Td2oANSacnZCeghnrr3OHBi6UPVPU+GFrymwaDqwQspACilLRmRnM7aTjNPA==

symbol-tree@^3.2.4:
version "3.2.4"
Expand Down

0 comments on commit bcd58eb

Please sign in to comment.