diff --git a/.changeset/busy-clouds-sink.md b/.changeset/busy-clouds-sink.md new file mode 100644 index 00000000000..36f4c3d0fc4 --- /dev/null +++ b/.changeset/busy-clouds-sink.md @@ -0,0 +1,5 @@ +--- +'@sap-ux/eslint-plugin-fiori-tools': patch +--- + +Update table configuration related eslint rules to include object page section label in the reported issue message. diff --git a/packages/eslint-plugin-fiori-tools/src/language/diagnostics.ts b/packages/eslint-plugin-fiori-tools/src/language/diagnostics.ts index 66bfda5e298..e44c8f66653 100644 --- a/packages/eslint-plugin-fiori-tools/src/language/diagnostics.ts +++ b/packages/eslint-plugin-fiori-tools/src/language/diagnostics.ts @@ -21,6 +21,7 @@ export interface WidthIncludingColumnHeaderDiagnostic { type: typeof WIDTH_INCLUDING_COLUMN_HEADER_RULE_TYPE; manifest: ManifestPropertyDiagnosticData; pageName: string; + pageSectionName?: string; annotation: { file: string; annotationPath: string; @@ -54,6 +55,7 @@ export type CreateModeMessageId = export interface CreationModeForTable { type: typeof CREATION_MODE_FOR_TABLE; pageName: string; + pageSectionName?: string; manifest: ManifestPropertyDiagnosticData; messageId: CreateModeMessageId; tableType: string; @@ -64,18 +66,21 @@ export interface CreationModeForTable { export interface CopyToClipboard { type: typeof COPY_TO_CLIPBOARD; pageName: string; + pageSectionName?: string; manifest: ManifestPropertyDiagnosticData; } export interface EnableExport { type: typeof ENABLE_EXPORT; pageName: string; + pageSectionName?: string; manifest: ManifestPropertyDiagnosticData; } export interface EnablePaste { type: typeof ENABLE_PASTE; pageName: string; + pageSectionName?: string; manifest: ManifestPropertyDiagnosticData; } @@ -107,6 +112,7 @@ export interface TablePersonalization { property?: PersonalizationProperty; undefinedProperties?: PersonalizationProperty[]; pageName: string; + pageSectionName?: string; manifest: ManifestPropertyDiagnosticData; } @@ -130,6 +136,7 @@ export interface NoDataFieldIntentBasedNavigation { export interface CondensedTableLayout { type: typeof CONDENSED_TABLE_LAYOUT; pageName: string; + pageSectionName?: string; manifest: ManifestPropertyDiagnosticData; } diff --git a/packages/eslint-plugin-fiori-tools/src/project-context/linker/annotations.ts b/packages/eslint-plugin-fiori-tools/src/project-context/linker/annotations.ts index 6de1ae9a388..d7bb6345424 100644 --- a/packages/eslint-plugin-fiori-tools/src/project-context/linker/annotations.ts +++ b/packages/eslint-plugin-fiori-tools/src/project-context/linker/annotations.ts @@ -14,6 +14,19 @@ import type { IndexedAnnotation, ParsedService } from '../parser'; import { buildAnnotationIndexKey } from '../parser'; import { UI_FIELD_GROUP, UI_LINE_ITEM } from '../../constants'; +/** + * index - Index of annotation + * referencedEntityType - Entity type + * qualifier - FieldGroup annotation qualifier + * sectionLabel - Object page section label + */ +type SectionConfig = { + index: number; + referencedEntityType: string; + qualifier?: string; + sectionLabel?: string; +}; + /** * Creates a configuration key from an annotation path * @@ -28,6 +41,7 @@ export function getConfigurationKey(annotationPath: string): string { export interface AnnotationBasedNode { type: T; annotation: IndexedAnnotation; + label?: string; /** * Path used by Fiori elements to reference this control */ @@ -186,12 +200,28 @@ function processReferenceFacetRecord( const [, _annotationPath] = fullyQualifiedPath.split('@'); const [term, qualifier] = _annotationPath.split('#'); + const propValues = elementsWithName(Edm.PropertyValue, record); + const propValue = propValues.find((p) => p.attributes.Property?.value === 'Label'); + const sectionLabel = propValue?.attributes?.String?.value; + if (term === UI_LINE_ITEM) { - return createTableSection(facets, index, referencedEntityType, qualifier, annotationPath, aliasInfo, service); + return createTableSection( + facets, + { index, referencedEntityType, qualifier, sectionLabel }, + annotationPath, + aliasInfo, + service + ); } if (term === UI_FIELD_GROUP) { - return addHeaderSection(facets, index, referencedEntityType, qualifier, annotationPath, aliasInfo, service); + return addHeaderSection( + facets, + { index, referencedEntityType, qualifier, sectionLabel }, + annotationPath, + aliasInfo, + service + ); } return undefined; @@ -228,36 +258,34 @@ function getReferencedEntityType( * Create a table section node with its child table node. * * @param facets - * @param index - * @param referencedEntityType - * @param qualifier + * @param config * @param annotationPath * @param aliasInfo * @param service + * @returns */ function createTableSection( facets: IndexedAnnotation, - index: number, - referencedEntityType: string, - qualifier: string | undefined, + config: SectionConfig, annotationPath: string, aliasInfo: AliasInformation, service: ParsedService ): TableSectionNode | undefined { const section: TableSectionNode = { type: 'table-section', - annotationPath: `@com.sap.vocabularies.UI.v1.Facets/${index}`, + annotationPath: `@com.sap.vocabularies.UI.v1.Facets/${config.index}`, + label: config.sectionLabel, annotation: facets, children: [] }; - const lineItemKey = buildAnnotationIndexKey(referencedEntityType, UI_LINE_ITEM); + const lineItemKey = buildAnnotationIndexKey(config.referencedEntityType, UI_LINE_ITEM); const tableAnnotations = service.index.annotations[lineItemKey]; if (!tableAnnotations) { return undefined; } - const annotation = tableAnnotations[qualifier ?? 'undefined']; + const annotation = tableAnnotations[config.qualifier ?? 'undefined']; if (!annotation) { return undefined; } @@ -280,9 +308,7 @@ function createTableSection( * Creates a header facet section node with field group child annotation. * * @param headerFacets - Header facet annotation - * @param index - Index of annotation - * @param referencedEntityType - Entity type - * @param qualifier - FieldGroup annotation qualifier + * @param config - Section configuration * @param annotationPath - Header facet annotation path * @param aliasInfo - Alias information for resolving namespaces * @param service - The parsed OData service @@ -290,27 +316,26 @@ function createTableSection( */ function addHeaderSection( headerFacets: IndexedAnnotation, - index: number, - referencedEntityType: string, - qualifier: string | undefined, + config: SectionConfig, annotationPath: string, aliasInfo: AliasInformation, service: ParsedService ): HeaderSectionNode | undefined { const section: HeaderSectionNode = { type: 'header-section', - annotationPath: `@com.sap.vocabularies.UI.v1.HeaderFacet/${index}`, + annotationPath: `@com.sap.vocabularies.UI.v1.HeaderFacet/${config.index}`, annotation: headerFacets, + label: config.sectionLabel, children: [] }; - const fieldGroupKey = buildAnnotationIndexKey(referencedEntityType, UI_FIELD_GROUP); + const fieldGroupKey = buildAnnotationIndexKey(config.referencedEntityType, UI_FIELD_GROUP); const fieldGroupAnnotations = service.index.annotations[fieldGroupKey]; if (!fieldGroupAnnotations) { return undefined; } - const annotation = fieldGroupAnnotations[qualifier ?? 'undefined']; + const annotation = fieldGroupAnnotations[config.qualifier ?? 'undefined']; if (!annotation) { return undefined; } diff --git a/packages/eslint-plugin-fiori-tools/src/project-context/linker/fe-v2.ts b/packages/eslint-plugin-fiori-tools/src/project-context/linker/fe-v2.ts index dda78961812..29d4d446f9a 100644 --- a/packages/eslint-plugin-fiori-tools/src/project-context/linker/fe-v2.ts +++ b/packages/eslint-plugin-fiori-tools/src/project-context/linker/fe-v2.ts @@ -72,7 +72,7 @@ export interface AnnotationBasedNode< Children = never > extends ConfigurationBase { annotation?: T; - + label?: string; children: Children[]; } diff --git a/packages/eslint-plugin-fiori-tools/src/project-context/linker/fe-v4.ts b/packages/eslint-plugin-fiori-tools/src/project-context/linker/fe-v4.ts index 0264af3521a..933bfb5504f 100644 --- a/packages/eslint-plugin-fiori-tools/src/project-context/linker/fe-v4.ts +++ b/packages/eslint-plugin-fiori-tools/src/project-context/linker/fe-v4.ts @@ -45,6 +45,7 @@ export interface AnnotationBasedNode< Children = never > extends ConfigurationBase { annotation?: T; + label?: string; children: Children[]; } diff --git a/packages/eslint-plugin-fiori-tools/src/rules/sap-copy-to-clipboard.ts b/packages/eslint-plugin-fiori-tools/src/rules/sap-copy-to-clipboard.ts index ccd687ce052..5e09e6d3cff 100644 --- a/packages/eslint-plugin-fiori-tools/src/rules/sap-copy-to-clipboard.ts +++ b/packages/eslint-plugin-fiori-tools/src/rules/sap-copy-to-clipboard.ts @@ -2,10 +2,11 @@ import type { FioriRuleDefinition } from '../types'; import { COPY_TO_CLIPBOARD, type CopyToClipboard } from '../language/diagnostics'; import { createFioriRule } from '../language/rule-factory'; import type { MemberNode } from '@humanwhocodes/momoa'; -import type { FeV2PageType } from '../project-context/linker/fe-v2'; +import type { FeV2PageType, Table as TableV2 } from '../project-context/linker/fe-v2'; import type { ParsedApp } from '../project-context/parser'; -import type { FeV4PageType } from '../project-context/linker/fe-v4'; +import type { FeV4PageType, Table as TableV4 } from '../project-context/linker/fe-v4'; import { createJsonFixer } from '../language/rule-fixer'; +import { checkAppTablesConfiguration } from '../utils/helpers'; const rule: FioriRuleDefinition = createFioriRule({ ruleId: COPY_TO_CLIPBOARD, @@ -18,7 +19,7 @@ const rule: FioriRuleDefinition = createFioriRule({ }, messages: { [COPY_TO_CLIPBOARD]: - 'Copy To Clipboard must be correctly configured. If not set, the "Copy" button is displayed' + 'Copy To Clipboard in the {{sectionText}}table must be correctly configured. If not set, the "Copy" button is displayed' }, fixable: 'code' }, @@ -32,14 +33,8 @@ const rule: FioriRuleDefinition = createFioriRule({ if (!parsedService) { continue; } - if (app.type === 'fe-v2') { - for (const page of app.pages) { - problems.push(...handleCopyInTableV2(page, parsedApp)); - } - } else if (app.type === 'fe-v4') { - for (const page of app.pages) { - problems.push(...handleDisableCopyInTableV4(page, parsedApp)); - } + for (const page of app.pages) { + problems.push(...(checkAppTablesConfiguration(page, parsedApp, checkConfiguration))); } } return problems; @@ -49,6 +44,9 @@ const rule: FioriRuleDefinition = createFioriRule({ context.report({ node, messageId: COPY_TO_CLIPBOARD, + data: { + sectionText: diagnostic.pageSectionName ? `${diagnostic.pageSectionName} ` : '' + }, fix: createJsonFixer({ context, deepestPathResult, @@ -60,53 +58,50 @@ const rule: FioriRuleDefinition = createFioriRule({ }); /** - * Looks through V2 app page tables and returns problems if copy is set to false. * - * @param page - V2 app page - * @param parsedApp - parsed V2 app - * @returns - CopyToClipoard issues + * @param table + * @returns */ -function handleCopyInTableV2(page: FeV2PageType, parsedApp: ParsedApp): CopyToClipboard[] { - const problems: CopyToClipboard[] = []; - for (const table of page.lookup['table'] ?? []) { - if (table.configuration.copy.valueInFile === false) { - problems.push({ - type: COPY_TO_CLIPBOARD, - pageName: page.targetName, - manifest: { - uri: parsedApp.manifest.manifestUri, - object: parsedApp.manifestObject, - propertyPath: table.configuration.copy.configurationPath - } - }); - } - } - return problems; +function isV2Table(table: TableV2 | TableV4): table is TableV2 { + return 'copy' in (table as TableV2).configuration; } /** - * Looks through V4 app page tables and returns problems if disableCopyToClipboard is set to true. * - * @param page - V4 app page - * @param parsedApp - parsed V4 app - * @returns - CopyToClipoard issues + * @param page + * @param table + * @param parsedApp + * @param problems + * @param pageSectionName */ -function handleDisableCopyInTableV4(page: FeV4PageType, parsedApp: ParsedApp): CopyToClipboard[] { - const problems: CopyToClipboard[] = []; - for (const table of page.lookup['table'] ?? []) { - if (table.configuration.disableCopyToClipboard.valueInFile === true) { - problems.push({ - type: COPY_TO_CLIPBOARD, - pageName: page.targetName, - manifest: { - uri: parsedApp.manifest.manifestUri, - object: parsedApp.manifestObject, - propertyPath: table.configuration.disableCopyToClipboard.configurationPath - } - }); - } +function checkConfiguration( + page: FeV4PageType | FeV2PageType, + table: TableV4 | TableV2, + parsedApp: ParsedApp, + problems: CopyToClipboard[], + pageSectionName?: string +): void { + let config; + let wrongValue = false; + if (isV2Table(table)) { + config = table.configuration.copy; + } else { + config = table.configuration.disableCopyToClipboard; + wrongValue = true; + } + if (config?.valueInFile === wrongValue) { + const copyIssue: CopyToClipboard | undefined = { + type: COPY_TO_CLIPBOARD, + pageName: page.targetName, + pageSectionName, + manifest: { + uri: parsedApp.manifest.manifestUri, + object: parsedApp.manifestObject, + propertyPath: config.configurationPath + } + }; + problems.push(copyIssue); } - return problems; } export default rule; diff --git a/packages/eslint-plugin-fiori-tools/src/rules/sap-creation-mode-for-table.ts b/packages/eslint-plugin-fiori-tools/src/rules/sap-creation-mode-for-table.ts index 62003621a4a..8fc4c1d84a5 100644 --- a/packages/eslint-plugin-fiori-tools/src/rules/sap-creation-mode-for-table.ts +++ b/packages/eslint-plugin-fiori-tools/src/rules/sap-creation-mode-for-table.ts @@ -28,6 +28,7 @@ interface CreateModeConfig { * @param options - Diagnostic options * @param options.messageId - The message identifier for the diagnostic * @param options.pageName - Name of the page where the issue occurs + * @param options.pageSectionName - Name of the object page section where the issue occurs * @param options.parsedApp - Parsed application context * @param options.configurationPath - Path to the configuration in manifest * @param options.tableType - Type of table (for example, 'GridTable' or 'ResponsiveTable') @@ -39,6 +40,7 @@ function reportDiagnostic( { messageId, pageName, + pageSectionName, parsedApp, configurationPath, tableType, @@ -47,6 +49,7 @@ function reportDiagnostic( }: { messageId: CreateModeMessageId; pageName: string; + pageSectionName?: string; parsedApp: ParsedApp; configurationPath: string[]; tableType: string; @@ -58,6 +61,7 @@ function reportDiagnostic( type: CREATION_MODE_FOR_TABLE, messageId, pageName, + pageSectionName, tableType, validValues, recommendedValue, @@ -128,6 +132,7 @@ function checkAnalyticalTableV2( * @param parsedApp - Parsed application context * @param tableType - Type of the table * @param problems - Array to collect diagnostic problems + * @param pageSectionName - Name of the object page section where the issue occurs * @returns True, if a configuration is found and an issue reported. False, if no configuration exists */ function validateCreateModeV2( @@ -135,7 +140,8 @@ function validateCreateModeV2( pageName: string, parsedApp: ParsedApp, tableType: string, - problems: CreationModeForTable[] + problems: CreationModeForTable[], + pageSectionName?: string ): boolean { if (!createMode.valueInFile) { return false; @@ -149,7 +155,8 @@ function validateCreateModeV2( configurationPath: createMode.configurationPath, tableType, validValues: createMode.values, - recommendedValue: RECOMMENDED_MODE_V2 + recommendedValue: RECOMMENDED_MODE_V2, + pageSectionName }); return true; } @@ -161,7 +168,8 @@ function validateCreateModeV2( parsedApp, configurationPath: createMode.configurationPath, tableType, - recommendedValue: RECOMMENDED_MODE_V2 + recommendedValue: RECOMMENDED_MODE_V2, + pageSectionName }); } return true; @@ -176,13 +184,15 @@ function validateCreateModeV2( * @param appCreateMode - Application-level create mode configuration * @param parsedApp - Parsed application context * @param problems - Array to collect diagnostic problems + * @param pageSectionName - Name of the object page section where the issue occurs */ function processTableV2( table: V2Table, page: FeV2ObjectPage, appCreateMode: CreateModeConfig, parsedApp: ParsedApp, - problems: CreationModeForTable[] + problems: CreationModeForTable[], + pageSectionName?: string ): void { const sectionCreateMode = table.configuration.createMode; const pageCreateMode = page.configuration.createMode; @@ -204,7 +214,7 @@ function processTableV2( } // Check section, page, then app level - if (validateCreateModeV2(sectionCreateMode, page.targetName, parsedApp, tableType, problems)) { + if (validateCreateModeV2(sectionCreateMode, page.targetName, parsedApp, tableType, problems, pageSectionName)) { return; } if (validateCreateModeV2(pageCreateMode, page.targetName, parsedApp, tableType, problems)) { @@ -282,7 +292,9 @@ function getRecommendedValueV4(tableType: string): string { * Checks if the value is valid and recommends a value based on best practices and the table type. * * @param creationMode - Creation mode configuration to validate - * @param pageName - Name of the page + * @param page - page information + * @param page.name - Name of the page + * @param page.sectionName - Name of the object page section * @param parsedApp - Parsed application context * @param tableType - Type of the table * @param recommendedValue - The recommended value for this table type @@ -292,7 +304,7 @@ function getRecommendedValueV4(tableType: string): string { */ function validateCreationModeV4( creationMode: CreateModeConfig, - pageName: string, + page: { name: string; sectionName?: string }, parsedApp: ParsedApp, tableType: string, recommendedValue: string, @@ -308,12 +320,13 @@ function validateCreationModeV4( if (!value) { reportDiagnostic(problems, { messageId: 'invalidCreateModeV4', - pageName, + pageName: page.name, parsedApp, configurationPath: creationMode.configurationPath, tableType, validValues, - recommendedValue + recommendedValue, + pageSectionName: page.sectionName }); return true; } @@ -321,12 +334,13 @@ function validateCreationModeV4( if (!validValues.includes(value)) { reportDiagnostic(problems, { messageId: 'invalidCreateModeV4', - pageName, + pageName: page.name, parsedApp, configurationPath: creationMode.configurationPath, tableType, validValues, - recommendedValue + recommendedValue, + pageSectionName: page.sectionName }); return true; } @@ -334,12 +348,13 @@ function validateCreationModeV4( if (value !== recommendedValue) { reportDiagnostic(problems, { messageId: 'recommendInlineCreationRowsV4', - pageName, + pageName: page.name, parsedApp, configurationPath: creationMode.configurationPath, tableType, validValues, - recommendedValue + recommendedValue, + pageSectionName: page.sectionName }); } return true; @@ -356,6 +371,7 @@ function validateCreationModeV4( * @param parsedApp - Parsed application context * @param problems - Array to collect diagnostic problems * @param shouldSuggestAppLevel - Whether to suggest app-level configuration (only if all tables have same recommended value) + * @param pageSectionName - Name of the object page section where the issue occurs */ function processTableV4( table: V4Table, @@ -363,7 +379,8 @@ function processTableV4( appCreateMode: CreateModeConfig, parsedApp: ParsedApp, problems: CreationModeForTable[], - shouldSuggestAppLevel: boolean + shouldSuggestAppLevel: boolean, + pageSectionName?: string ): void { const tableCreationMode = table.configuration.creationMode; const tableType = table.configuration.tableType?.valueInFile ?? ''; @@ -379,7 +396,7 @@ function processTableV4( if ( validateCreationModeV4( tableCreationMode, - page.targetName, + { name: page.targetName, sectionName: pageSectionName }, parsedApp, tableType, recommendedValue, @@ -463,9 +480,11 @@ function processV2Apps( if (page.type !== 'object-page') { continue; } - - for (const table of page.lookup['table'] ?? []) { - processTableV2(table, page, appCreateMode, parsedApp, problems); + for (const tableSection of page.sections.filter((section) => section.type === 'table-section')) { + const table = tableSection.children.find((element) => element.type === 'table'); + if (table) { + processTableV2(table, page, appCreateMode, parsedApp, problems, tableSection.annotation?.label); + } } } } @@ -530,9 +549,19 @@ function processV4Apps( if (page.type !== 'object-page') { continue; } - - for (const table of page.lookup['table'] ?? []) { - processTableV4(table, page, appCreateMode, parsedApp, problems, shouldSuggestAppLevel); + for (const tableSection of page.sections.filter((section) => section.type === 'table-section')) { + const table = tableSection.children.find((element) => element.type === 'table'); + if (table) { + processTableV4( + table, + page, + appCreateMode, + parsedApp, + problems, + shouldSuggestAppLevel, + tableSection.annotation?.label + ); + } } } } @@ -550,13 +579,13 @@ const rule: FioriRuleDefinition = createFioriRule 0 ? ` Valid values are: ${diagnostic.validValues.join(', ')}.` : '', - recommendedValue: diagnostic.recommendedValue ?? '' + recommendedValue: diagnostic.recommendedValue ?? '', + sectionText: diagnostic.pageSectionName ? ` in ${diagnostic.pageSectionName} section` : '' }, fix: createJsonFixer({ value: operation === 'delete' ? undefined : diagnostic.recommendedValue, diff --git a/packages/eslint-plugin-fiori-tools/src/rules/sap-enable-export.ts b/packages/eslint-plugin-fiori-tools/src/rules/sap-enable-export.ts index e11a25a178d..3b3ca2dc303 100644 --- a/packages/eslint-plugin-fiori-tools/src/rules/sap-enable-export.ts +++ b/packages/eslint-plugin-fiori-tools/src/rules/sap-enable-export.ts @@ -3,8 +3,9 @@ import { ENABLE_EXPORT, type EnableExport } from '../language/diagnostics'; import { createFioriRule } from '../language/rule-factory'; import type { MemberNode } from '@humanwhocodes/momoa'; import type { ParsedApp } from '../project-context/parser'; -import type { FeV4PageType } from '../project-context/linker/fe-v4'; +import type { FeV4PageType, Table } from '../project-context/linker/fe-v4'; import { createJsonFixer } from '../language/rule-fixer'; +import { checkAppTablesConfiguration } from '../utils/helpers'; const rule: FioriRuleDefinition = createFioriRule({ ruleId: ENABLE_EXPORT, @@ -16,7 +17,7 @@ const rule: FioriRuleDefinition = createFioriRule({ url: 'https://github.com/SAP/open-ux-tools/blob/main/packages/eslint-plugin-fiori-tools/docs/rules/sap-enable-export.md' }, messages: { - [ENABLE_EXPORT]: 'Export functionality in the table must be enabled' + [ENABLE_EXPORT]: 'Export functionality in the {{sectionText}}table must be enabled' }, fixable: 'code' }, @@ -32,7 +33,9 @@ const rule: FioriRuleDefinition = createFioriRule({ } if (app.type === 'fe-v4') { for (const page of app.pages) { - problems.push(...handleExportInTableV4(page, parsedApp)); + problems.push( + ...(checkAppTablesConfiguration(page, parsedApp, checkConfiguration)) + ); } } } @@ -43,6 +46,7 @@ const rule: FioriRuleDefinition = createFioriRule({ context.report({ node, messageId: ENABLE_EXPORT, + data: { sectionText: diagnostic.pageSectionName ? `${diagnostic.pageSectionName} ` : '' }, fix: createJsonFixer({ context, deepestPathResult, @@ -54,28 +58,31 @@ const rule: FioriRuleDefinition = createFioriRule({ }); /** - * Looks through V4 app page tables and returns problems if enableExport is set to false. * - * @param page - V4 app page - * @param parsedApp - parsed V4 app - * @returns - EnableExport issues + * @param page + * @param table + * @param parsedApp + * @param problems + * @param pageSectionName */ -function handleExportInTableV4(page: FeV4PageType, parsedApp: ParsedApp): EnableExport[] { - const problems: EnableExport[] = []; - for (const table of page.lookup['table'] ?? []) { - if (table.configuration.enableExport.valueInFile === false) { - problems.push({ - type: ENABLE_EXPORT, - pageName: page.targetName, - manifest: { - uri: parsedApp.manifest.manifestUri, - object: parsedApp.manifestObject, - propertyPath: table.configuration.enableExport.configurationPath - } - }); - } +function checkConfiguration( + page: FeV4PageType, + table: Table, + parsedApp: ParsedApp, + problems: EnableExport[], + pageSectionName?: string +): void { + if (table.configuration.enableExport.valueInFile === false) { + problems.push({ + type: ENABLE_EXPORT, + pageName: page.targetName, + pageSectionName, + manifest: { + uri: parsedApp.manifest.manifestUri, + object: parsedApp.manifestObject, + propertyPath: table.configuration.enableExport.configurationPath + } + }); } - return problems; } - export default rule; diff --git a/packages/eslint-plugin-fiori-tools/src/rules/sap-enable-paste.ts b/packages/eslint-plugin-fiori-tools/src/rules/sap-enable-paste.ts index ccb2271b96f..a9c51cc0ea4 100644 --- a/packages/eslint-plugin-fiori-tools/src/rules/sap-enable-paste.ts +++ b/packages/eslint-plugin-fiori-tools/src/rules/sap-enable-paste.ts @@ -3,8 +3,9 @@ import { ENABLE_PASTE, type EnablePaste } from '../language/diagnostics'; import { createFioriRule } from '../language/rule-factory'; import type { MemberNode } from '@humanwhocodes/momoa'; import type { ParsedApp } from '../project-context/parser'; -import type { FeV4PageType } from '../project-context/linker/fe-v4'; +import type { FeV4PageType, Table } from '../project-context/linker/fe-v4'; import { createJsonFixer } from '../language/rule-fixer'; +import { checkAppTablesConfiguration } from '../utils/helpers'; const rule: FioriRuleDefinition = createFioriRule({ ruleId: ENABLE_PASTE, @@ -16,7 +17,7 @@ const rule: FioriRuleDefinition = createFioriRule({ url: 'https://github.com/SAP/open-ux-tools/blob/main/packages/eslint-plugin-fiori-tools/docs/rules/sap-enable-paste.md' }, messages: { - [ENABLE_PASTE]: 'Paste functionality in the table must be enabled' + [ENABLE_PASTE]: 'Paste functionality in the {{sectionText}}table must be enabled' }, fixable: 'code' }, @@ -35,7 +36,7 @@ const rule: FioriRuleDefinition = createFioriRule({ if (page.type !== 'object-page') { continue; } - problems.push(...handlePasteInTableV4(page, parsedApp)); + problems.push(...(checkAppTablesConfiguration(page, parsedApp, checkConfiguration))); } } } @@ -46,6 +47,7 @@ const rule: FioriRuleDefinition = createFioriRule({ context.report({ node, messageId: ENABLE_PASTE, + data: { sectionText: diagnostic.pageSectionName ? `${diagnostic.pageSectionName} ` : '' }, fix: createJsonFixer({ context, deepestPathResult, @@ -57,28 +59,32 @@ const rule: FioriRuleDefinition = createFioriRule({ }); /** - * Looks through V4 app page tables and returns problems if enablePaste is set to false. * - * @param page - V4 app page - * @param parsedApp - parsed V4 app - * @returns - EnablePaste issues + * @param page + * @param table + * @param parsedApp + * @param problems + * @param pageSectionName */ -function handlePasteInTableV4(page: FeV4PageType, parsedApp: ParsedApp): EnablePaste[] { - const problems: EnablePaste[] = []; - for (const table of page.lookup['table'] ?? []) { - if (table.configuration.enablePaste.valueInFile === false) { - problems.push({ - type: ENABLE_PASTE, - pageName: page.targetName, - manifest: { - uri: parsedApp.manifest.manifestUri, - object: parsedApp.manifestObject, - propertyPath: table.configuration.enablePaste.configurationPath - } - }); - } +function checkConfiguration( + page: FeV4PageType, + table: Table, + parsedApp: ParsedApp, + problems: EnablePaste[], + pageSectionName?: string +): void { + if (table.configuration.enablePaste.valueInFile === false) { + problems.push({ + type: ENABLE_PASTE, + pageName: page.targetName, + pageSectionName, + manifest: { + uri: parsedApp.manifest.manifestUri, + object: parsedApp.manifestObject, + propertyPath: table.configuration.enablePaste.configurationPath + } + }); } - return problems; } export default rule; diff --git a/packages/eslint-plugin-fiori-tools/src/rules/sap-table-personalization.ts b/packages/eslint-plugin-fiori-tools/src/rules/sap-table-personalization.ts index 451a793502a..533581db1e0 100644 --- a/packages/eslint-plugin-fiori-tools/src/rules/sap-table-personalization.ts +++ b/packages/eslint-plugin-fiori-tools/src/rules/sap-table-personalization.ts @@ -42,12 +42,13 @@ const rule: FioriRuleDefinition = createFioriRule({ }, messages: { [TABLE_PERSONALIZATION]: - 'Table personalization should be enabled. Currently every table personalization setting is disabled.', - [TABLE_PERSONALIZATION_COLUMN]: 'Adding or removing table columns should be enabled.', - [TABLE_PERSONALIZATION_FILTER]: 'Table data filtering should be enabled.', - [TABLE_PERSONALIZATION_SORT]: 'Table data sorting should be enabled.', + 'Table personalization should be enabled. Currently every{{sectionText}} table personalization setting is disabled.', + [TABLE_PERSONALIZATION_COLUMN]: + 'Adding or removing table columns should be enabled in the{{sectionText}} table.', + [TABLE_PERSONALIZATION_FILTER]: 'Data filtering should be enabled in the{{sectionText}} table.', + [TABLE_PERSONALIZATION_SORT]: 'Data sorting should be enabled in the{{sectionText}} table.', [TABLE_PERSONALIZATION_GROUP]: - 'Table data grouping should be enabled for analytical and responsive type tables.', + 'Data grouping should be enabled in the{{sectionText}} table. Grouping is available for analytical and responsive type tables.', [MISSING_PERSONALIZATION_PROPERTIES]: 'In case of using an object, omitting a setting is treated as false. {{undefinedPropertiesString}}.' }, @@ -68,10 +69,7 @@ const rule: FioriRuleDefinition = createFioriRule({ } for (const page of app.pages) { - for (const table of page.lookup['table'] ?? []) { - const tableProblems = checkPersonalizationValue(table, page, parsedApp); - problems.push(...tableProblems); - } + checkTableConfiguration(page, parsedApp, problems); } } return problems; @@ -82,13 +80,15 @@ const rule: FioriRuleDefinition = createFioriRule({ let undefinedPropertiesString = ''; let messageId = MessageIdByProperty[diagnostic.property ?? '']; if (diagnostic.undefinedProperties?.length) { - undefinedPropertiesString = `Currently ${diagnostic.undefinedProperties.join(', ')} ${diagnostic.undefinedProperties.length === 1 ? 'is disabled' : 'are disabled'}`; + const tableReference = diagnostic.pageSectionName ? ` ${diagnostic.pageSectionName} table` : ' table'; + undefinedPropertiesString = `Currently ${diagnostic.undefinedProperties.join(', ')} ${diagnostic.undefinedProperties.length === 1 ? 'is disabled' : 'are disabled'} in the${tableReference}`; messageId = MISSING_PERSONALIZATION_PROPERTIES; } return context.report({ node, data: { - undefinedPropertiesString + undefinedPropertiesString, + sectionText: diagnostic.pageSectionName ? ` ${diagnostic.pageSectionName}` : '' }, messageId, fix: createJsonFixer({ @@ -139,9 +139,15 @@ function shouldCheckProperty(table: Table, parsedApp: ParsedApp, propertyName: P * @param table - OData V4 table. * @param page - OData V4 page. * @param parsedApp - Parsed application. + * @param pageSectionName * @returns TablePersonalization issues collected for all properties */ -function checkPersonalizationValue(table: Table, page: FeV4PageType, parsedApp: ParsedApp): TablePersonalization[] { +function checkPersonalizationValue( + table: Table, + page: FeV4PageType, + parsedApp: ParsedApp, + pageSectionName?: string +): TablePersonalization[] { const problems: TablePersonalization[] = []; const personalization = table.configuration.personalization.valueInFile; @@ -156,6 +162,7 @@ function checkPersonalizationValue(table: Table, page: FeV4PageType, parsedApp: { type: TABLE_PERSONALIZATION, pageName: page.targetName, + pageSectionName, messageId: MessageIdByProperty[''], manifest: { uri: parsedApp.manifest.manifestUri, @@ -175,6 +182,7 @@ function checkPersonalizationValue(table: Table, page: FeV4PageType, parsedApp: problems.push({ type: TABLE_PERSONALIZATION, pageName: page.targetName, + pageSectionName, property, messageId: MessageIdByProperty[property], manifest: { @@ -192,6 +200,7 @@ function checkPersonalizationValue(table: Table, page: FeV4PageType, parsedApp: problems.push({ type: TABLE_PERSONALIZATION, pageName: page.targetName, + pageSectionName, undefinedProperties, messageId: MISSING_PERSONALIZATION_PROPERTIES, manifest: { @@ -204,4 +213,27 @@ function checkPersonalizationValue(table: Table, page: FeV4PageType, parsedApp: return problems; } +/** + * + * @param page + * @param parsedApp + * @param problems + */ +function checkTableConfiguration(page: FeV4PageType, parsedApp: ParsedApp, problems: TablePersonalization[]): void { + if (page.type === 'list-report-page') { + for (const table of page.lookup['table'] ?? []) { + const tableProblems = checkPersonalizationValue(table, page, parsedApp); + problems.push(...tableProblems); + } + } else if (page.type === 'object-page') { + for (const tableSection of page.sections.filter((section) => section.type === 'table-section')) { + const table = tableSection.children.find((element) => element.type === 'table'); + if (table) { + const tableProblems = checkPersonalizationValue(table, page, parsedApp, tableSection.annotation?.label); + problems.push(...tableProblems); + } + } + } +} + export default rule; diff --git a/packages/eslint-plugin-fiori-tools/src/rules/sap-width-including-column-header.ts b/packages/eslint-plugin-fiori-tools/src/rules/sap-width-including-column-header.ts index 8a129893a29..1dac30daf26 100644 --- a/packages/eslint-plugin-fiori-tools/src/rules/sap-width-including-column-header.ts +++ b/packages/eslint-plugin-fiori-tools/src/rules/sap-width-including-column-header.ts @@ -46,9 +46,50 @@ function shouldTableHaveWidthIncludingColumnHeader(table: Table, aliasInfo: Alia ); } +/** + * Checks table and adds diagnostic problems for tables that should have widthIncludingColumnHeader set. + * + * @param page - SAP Fiori elements for OData V4 page to check (object page or list report) + * @param table - A table with annotation + * @param parsedApp - Parsed application containing manifest.json file data + * @param parsedService - Parsed service containing metadata and annotations + * @param problems - Array to collect diagnostic problems + * @param tableSectionName - Label of the object page section + */ +function checkTable( + page: FeV4ListReport | FeV4ObjectPage, + table: Table | undefined, + parsedApp: ParsedApp, + parsedService: ParsedService, + problems: WidthIncludingColumnHeaderDiagnostic[], + tableSectionName?: string +): void { + if (!table?.annotation) { + return; + } + const aliasInfo = parsedService.artifacts.aliasInfo[table.annotation.annotation.top.uri]; + + if (shouldTableHaveWidthIncludingColumnHeader(table, aliasInfo)) { + problems.push({ + type: WIDTH_INCLUDING_COLUMN_HEADER_RULE_TYPE, + pageName: page.targetName, + pageSectionName: tableSectionName, + manifest: { + uri: parsedApp.manifest.manifestUri, + object: parsedApp.manifestObject, + propertyPath: table.configuration.widthIncludingColumnHeader.configurationPath + }, + annotation: { + file: table.annotation.annotation.source, + annotationPath: table.annotation.annotationPath, + reference: table.annotation.annotation.top + } + }); + } +} + /** * Checks tables in a page for widthIncludingColumnHeader configuration issues. - * Adds diagnostic problems for tables that should have this property set. * * @param page - SAP Fiori elements for OData V4 page to check (object page or list report) * @param parsedApp - Parsed application containing manifest.json file data @@ -56,32 +97,19 @@ function shouldTableHaveWidthIncludingColumnHeader(table: Table, aliasInfo: Alia * @param problems - Array to collect diagnostic problems */ function checkTablesInPage( - page: FeV4ObjectPage | FeV4ListReport, + page: FeV4ListReport | FeV4ObjectPage, parsedApp: ParsedApp, parsedService: ParsedService, problems: WidthIncludingColumnHeaderDiagnostic[] ): void { - for (const table of page.lookup['table'] ?? []) { - if (!table.annotation) { - continue; + if (page.type === 'list-report-page') { + for (const table of page.lookup['table'] ?? []) { + checkTable(page, table, parsedApp, parsedService, problems); } - const aliasInfo = parsedService.artifacts.aliasInfo[table.annotation.annotation.top.uri]; - - if (shouldTableHaveWidthIncludingColumnHeader(table, aliasInfo)) { - problems.push({ - type: WIDTH_INCLUDING_COLUMN_HEADER_RULE_TYPE, - pageName: page.targetName, - manifest: { - uri: parsedApp.manifest.manifestUri, - object: parsedApp.manifestObject, - propertyPath: table.configuration.widthIncludingColumnHeader.configurationPath - }, - annotation: { - file: table.annotation.annotation.source, - annotationPath: table.annotation.annotationPath, - reference: table.annotation.annotation.top - } - }); + } else if (page.type === 'object-page') { + for (const tableSection of page.sections.filter((section) => section.type === 'table-section')) { + const table = tableSection.children.find((element) => element.type === 'table'); + checkTable(page, table, parsedApp, parsedService, problems, tableSection.annotation?.label); } } } @@ -98,7 +126,7 @@ const rule: FioriRuleDefinition = createFioriRule({ }, messages: { ['width-including-column-header-manifest']: - 'Small tables (< 6 columns) should use widthIncludingColumnHeader: true for improved calculation of the column width. Add it to the control configuration for "{{table}}" table.', + 'Small tables (< 6 columns) should use widthIncludingColumnHeader: true for improved calculation of the column width. Add it to the control configuration of the {{sectionText}}table.', ['width-including-column-header']: 'Small tables (< 6 columns) should use widthIncludingColumnHeader: true for improved calculation of the column width.' }, @@ -136,7 +164,7 @@ const rule: FioriRuleDefinition = createFioriRule({ node, messageId: 'width-including-column-header-manifest', data: { - table: diagnostic.annotation.annotationPath + sectionText: diagnostic.pageSectionName ? `${diagnostic.pageSectionName} ` : '' }, fix: createJsonFixer({ context, node, deepestPathResult: paths, value: true }) }); diff --git a/packages/eslint-plugin-fiori-tools/src/utils/helpers.ts b/packages/eslint-plugin-fiori-tools/src/utils/helpers.ts index 60cdd88306d..362a3bbc0e9 100644 --- a/packages/eslint-plugin-fiori-tools/src/utils/helpers.ts +++ b/packages/eslint-plugin-fiori-tools/src/utils/helpers.ts @@ -3,6 +3,9 @@ */ import type { Rule } from 'eslint'; +import type { FeV4PageType } from '../project-context/linker/fe-v4'; +import type { FeV2PageType } from '../project-context/linker/fe-v2'; +import type { ParsedApp } from '../project-context/parser'; // Type aliases for better readability export type ASTNode = Rule.Node; @@ -797,3 +800,38 @@ export function findDeepestExistingPath( missingSegments: [] }; } + +/** + * Cheks table setting configuration in a page. + * + * @param page - Application page + * @param parsedApp - Parsed application + * @param checkConfiguration - Function to check a specific property in the table configuration + * @returns Found rule diagnostic issues + */ +export function checkAppTablesConfiguration( + page: FeV4PageType | FeV2PageType, + parsedApp: ParsedApp, + checkConfiguration: ( + page: any, + table: any, + parsedApp: ParsedApp, + problems: DiagnosticType[], + pageSectionLabel?: string + ) => void +): DiagnosticType[] { + const problems: DiagnosticType[] = []; + if (page.type === 'list-report-page') { + for (const table of page.lookup['table'] ?? []) { + checkConfiguration(page, table, parsedApp, problems); + } + } else if (page.type === 'object-page') { + for (const tableSection of page.sections.filter((section) => section.type === 'table-section')) { + const table = tableSection.children.find((element) => element.type === 'table'); + if (table) { + checkConfiguration(page, table, parsedApp, problems, tableSection.annotation?.label); + } + } + } + return problems; +} diff --git a/packages/eslint-plugin-fiori-tools/test/data/v2-xml-start/webapp/annotations/annotation.xml b/packages/eslint-plugin-fiori-tools/test/data/v2-xml-start/webapp/annotations/annotation.xml index 255318ecfe1..0bec6ae8699 100644 --- a/packages/eslint-plugin-fiori-tools/test/data/v2-xml-start/webapp/annotations/annotation.xml +++ b/packages/eslint-plugin-fiori-tools/test/data/v2-xml-start/webapp/annotations/annotation.xml @@ -51,12 +51,12 @@ - + - - - + + + @@ -201,7 +201,7 @@ - + diff --git a/packages/eslint-plugin-fiori-tools/test/rules/sap-copy-to-clipboard.test.ts b/packages/eslint-plugin-fiori-tools/test/rules/sap-copy-to-clipboard.test.ts index aebe08fc3f3..ffffea1354b 100644 --- a/packages/eslint-plugin-fiori-tools/test/rules/sap-copy-to-clipboard.test.ts +++ b/packages/eslint-plugin-fiori-tools/test/rules/sap-copy-to-clipboard.test.ts @@ -121,7 +121,8 @@ ruleTester.run(TEST_NAME, copyToClipboardRule, { ]), errors: [ { - messageId: 'sap-copy-to-clipboard', + message: + 'Copy To Clipboard in the Products table must be correctly configured. If not set, the "Copy" button is displayed', line: 154, column: 23 } @@ -169,7 +170,8 @@ ruleTester.run(TEST_NAME, copyToClipboardRule, { ]), errors: [ { - messageId: 'sap-copy-to-clipboard', + message: + 'Copy To Clipboard in the table must be correctly configured. If not set, the "Copy" button is displayed', line: 127, column: 21 } diff --git a/packages/eslint-plugin-fiori-tools/test/rules/sap-creation-mode-for-table-v2.test.ts b/packages/eslint-plugin-fiori-tools/test/rules/sap-creation-mode-for-table-v2.test.ts index 64ff113582f..8c6d8e6f233 100644 --- a/packages/eslint-plugin-fiori-tools/test/rules/sap-creation-mode-for-table-v2.test.ts +++ b/packages/eslint-plugin-fiori-tools/test/rules/sap-creation-mode-for-table-v2.test.ts @@ -146,7 +146,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'invalidCreateMode' + message: + 'Invalid createMode value: "badValue" in Products section. The recommended value is "creationRows". Valid values are: creationRows, creationRowsHiddenInEditMode, newPage.' } ] }, @@ -188,7 +189,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'invalidCreateMode' + message: + 'Invalid createMode value: "badValue". The recommended value is "creationRows". Valid values are: creationRows, creationRowsHiddenInEditMode, newPage.' } ] }, @@ -212,7 +214,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'invalidCreateMode' + message: + 'Invalid createMode value: "badValue". The recommended value is "creationRows". Valid values are: creationRows, creationRowsHiddenInEditMode, newPage.' } ] }, @@ -275,7 +278,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'analyticalTableNotSupported' + message: + 'Creation mode is not supported for analytical tables. Remove the createMode or creationMode property.' } ] }, @@ -336,7 +340,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'analyticalTableNotSupported' + message: + 'Creation mode is not supported for analytical tables. Remove the createMode or creationMode property.' } ] }, @@ -360,7 +365,7 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'suggestAppLevel' + message: 'Consider adding createMode at the application level for a better user experience.' } ] }, @@ -413,7 +418,7 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'suggestAppLevel' + message: 'Consider adding createMode at the application level for a better user experience.' } ] }, diff --git a/packages/eslint-plugin-fiori-tools/test/rules/sap-creation-mode-for-table-v4.test.ts b/packages/eslint-plugin-fiori-tools/test/rules/sap-creation-mode-for-table-v4.test.ts index 21f0f59fb4a..bbf4895d1c5 100644 --- a/packages/eslint-plugin-fiori-tools/test/rules/sap-creation-mode-for-table-v4.test.ts +++ b/packages/eslint-plugin-fiori-tools/test/rules/sap-creation-mode-for-table-v4.test.ts @@ -355,7 +355,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'invalidCreateModeV4' + message: + 'Invalid creationMode value \"InvalidMode\" for ResponsiveTable in Incident Flow section. The recommended value is \"InlineCreationRows\". Valid values are: InlineCreationRows, NewPage.' } ] }, @@ -434,7 +435,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'recommendInlineCreationRowsV4' + message: + 'Consider using \"InlineCreationRows\" for a better user experience instead of \"NewPage\".' } ] }, @@ -513,7 +515,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'invalidCreateModeV4' + message: + 'Invalid creationMode value "InlineCreationRows" for Tree Table in Incident Flow section. The recommended value is "Inline". Valid values are: Inline, NewPage, CreationDialog.' } ] }, @@ -592,7 +595,7 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'recommendInlineCreationRowsV4' + message: 'Consider using "Inline" for a better user experience instead of "NewPage".' } ] }, @@ -654,7 +657,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'analyticalTableNotSupported' + message: + 'Creation mode is not supported for analytical tables. Remove the createMode or creationMode property.' } ] }, @@ -733,7 +737,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'recommendInlineCreationRowsV4' + message: + 'Consider using "InlineCreationRows" for a better user experience instead of "NewPage".' } ] }, @@ -811,7 +816,7 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'recommendInlineCreationRowsV4' + message: 'Consider using "Inline" for a better user experience instead of "CreationDialog".' } ] }, @@ -865,7 +870,7 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'suggestAppLevelV4' + message: 'Consider adding creationMode at the application level for better user experience.' } ] }, @@ -920,7 +925,7 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'recommendInlineCreationRowsV4' + message: 'Consider using "Inline" for a better user experience instead of "NewPage".' } ] }, @@ -974,7 +979,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'invalidCreateModeV4' + message: + 'Invalid creationMode value "InvalidMode" for ResponsiveTable. The recommended value is "InlineCreationRows". Valid values are: InlineCreationRows, NewPage.' } ] }, @@ -1060,7 +1066,8 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'recommendInlineCreationRowsV4' + message: + 'Consider using "InlineCreationRows" for a better user experience instead of "NewPage".' } ] }, @@ -1093,7 +1100,7 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'suggestAppLevelV4' + message: 'Consider adding creationMode at the application level for better user experience.' } ] }, @@ -1201,10 +1208,12 @@ ruleTester.run(TEST_NAME, createTableRule, { ]), errors: [ { - messageId: 'invalidCreateModeV4' + message: + 'Invalid creationMode value "tableSettings" for ResponsiveTable in Incident Flow section. The recommended value is "InlineCreationRows". Valid values are: InlineCreationRows, NewPage.' }, { - messageId: 'invalidCreateModeV4' + message: + 'Invalid creationMode value "tableSettings" for Tree Table in Category section. The recommended value is "Inline". Valid values are: Inline, NewPage, CreationDialog.' } ] }, diff --git a/packages/eslint-plugin-fiori-tools/test/rules/sap-enable-export.test.ts b/packages/eslint-plugin-fiori-tools/test/rules/sap-enable-export.test.ts index 085a43217bf..ae06bc26361 100644 --- a/packages/eslint-plugin-fiori-tools/test/rules/sap-enable-export.test.ts +++ b/packages/eslint-plugin-fiori-tools/test/rules/sap-enable-export.test.ts @@ -45,11 +45,11 @@ ruleTester.run(TEST_NAME, enableExportRule, { 'sap.ui5', 'routing', 'targets', - 'IncidentsList', + 'IncidentsObjectPage', 'options', 'settings', 'controlConfiguration', - '@com.sap.vocabularies.UI.v1.LineItem', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', 'tableSettings', 'enableExport' ], @@ -64,7 +64,7 @@ ruleTester.run(TEST_NAME, enableExportRule, { invalid: [ createInvalidTest( { - name: 'V4 - enableExport is false', + name: 'V4 - list report page - enableExport is false', filename: V4_MANIFEST_PATH, code: getManifestAsCode(V4_MANIFEST, [ { @@ -85,7 +85,7 @@ ruleTester.run(TEST_NAME, enableExportRule, { ]), errors: [ { - messageId: 'sap-enable-export', + message: 'Export functionality in the table must be enabled', line: 127, column: 21 } @@ -112,6 +112,55 @@ ruleTester.run(TEST_NAME, enableExportRule, { ]) }, [FACETSV4] + ), + createInvalidTest( + { + name: 'V4 - object page - enableExport is false', + filename: V4_MANIFEST_PATH, + code: getManifestAsCode(V4_MANIFEST, [ + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'enableExport' + ], + value: false + } + ]), + errors: [ + { + message: 'Export functionality in the Products table must be enabled', + line: 145, + column: 21 + } + ], + output: getManifestAsCode(V4_MANIFEST, [ + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings' + ], + value: { + // enableExport property removed + } + } + ]) + }, + [FACETSV4] ) ] }); diff --git a/packages/eslint-plugin-fiori-tools/test/rules/sap-enable-paste.test.ts b/packages/eslint-plugin-fiori-tools/test/rules/sap-enable-paste.test.ts index 68f62018360..e0858f2c17a 100644 --- a/packages/eslint-plugin-fiori-tools/test/rules/sap-enable-paste.test.ts +++ b/packages/eslint-plugin-fiori-tools/test/rules/sap-enable-paste.test.ts @@ -110,7 +110,7 @@ ruleTester.run(TEST_NAME, enablePasteRule, { ]), errors: [ { - messageId: 'sap-enable-paste', + message: 'Paste functionality in the Products table must be enabled', line: 145, column: 21 } diff --git a/packages/eslint-plugin-fiori-tools/test/rules/sap-table-personalization.test.ts b/packages/eslint-plugin-fiori-tools/test/rules/sap-table-personalization.test.ts index 6b0b63af29d..0c8f6e9aa8c 100644 --- a/packages/eslint-plugin-fiori-tools/test/rules/sap-table-personalization.test.ts +++ b/packages/eslint-plugin-fiori-tools/test/rules/sap-table-personalization.test.ts @@ -57,7 +57,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createValidTest( { @@ -129,7 +129,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createValidTest( { @@ -177,7 +177,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createValidTest( { @@ -225,7 +225,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createValidTest( { @@ -273,7 +273,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createValidTest( { @@ -321,7 +321,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createValidTest( { @@ -369,7 +369,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createValidTest( { @@ -417,7 +417,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ) ], @@ -445,7 +445,8 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { ]), errors: [ { - messageId: 'sap-table-personalization', + message: + 'Table personalization should be enabled. Currently every table personalization setting is disabled.', line: 127, column: 21 } @@ -468,7 +469,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createInvalidTest( { @@ -493,7 +494,8 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { ]), errors: [ { - messageId: 'sap-table-missing-personalization-properties', + message: + 'In case of using an object, omitting a setting is treated as false. Currently column, filter, sort are disabled in the Products table.', line: 145, column: 21 } @@ -565,22 +567,23 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { ]), errors: [ { - messageId: 'sap-table-personalization-column', + message: 'Adding or removing table columns should be enabled in the table.', line: 128, column: 23 }, { - messageId: 'sap-table-personalization-filter', + message: 'Data filtering should be enabled in the table.', line: 129, column: 23 }, { - messageId: 'sap-table-personalization-group', + message: + 'Data grouping should be enabled in the table. Grouping is available for analytical and responsive type tables.', line: 130, column: 23 }, { - messageId: 'sap-table-personalization-sort', + message: 'Data sorting should be enabled in the table.', line: 131, column: 23 } @@ -622,7 +625,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createInvalidTest( { @@ -671,7 +674,8 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { ]), errors: [ { - messageId: 'sap-table-personalization-group', + message: + 'Data grouping should be enabled in the table. Grouping is available for analytical and responsive type tables.', line: 130, column: 23 } @@ -713,7 +717,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createInvalidTest( { @@ -760,7 +764,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { line: 127, column: 21, message: - 'In case of using an object, omitting a setting is treated as false. Currently column, filter, group, sort are disabled.' + 'In case of using an object, omitting a setting is treated as false. Currently column, filter, group, sort are disabled in the table.' } ], output: getManifestAsCode(V4_MANIFEST, [ @@ -800,7 +804,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createInvalidTest( { @@ -831,12 +835,12 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { line: 127, column: 21, message: - 'In case of using an object, omitting a setting is treated as false. Currently column, sort are disabled.' + 'In case of using an object, omitting a setting is treated as false. Currently column, sort are disabled in the table.' }, { line: 129, column: 23, - message: 'Table data filtering should be enabled.' + message: 'Data filtering should be enabled in the table.' } ], output: getManifestAsCode(V4_MANIFEST, [ @@ -857,7 +861,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createInvalidTest( { @@ -889,12 +893,12 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { line: 127, column: 21, message: - 'In case of using an object, omitting a setting is treated as false. Currently column is disabled.' + 'In case of using an object, omitting a setting is treated as false. Currently column is disabled in the table.' }, { line: 129, column: 23, - message: 'Table data filtering should be enabled.' + message: 'Data filtering should be enabled in the table.' } ], output: getManifestAsCode(V4_MANIFEST, [ @@ -915,7 +919,7 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, - [FACETSV4] + [] ), createInvalidTest( { @@ -965,12 +969,13 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { line: 127, column: 21, message: - 'In case of using an object, omitting a setting is treated as false. Currently filter, sort are disabled.' + 'In case of using an object, omitting a setting is treated as false. Currently filter, sort are disabled in the table.' }, { line: 129, column: 23, - message: 'Table data grouping should be enabled for analytical and responsive type tables.' + message: + 'Data grouping should be enabled in the table. Grouping is available for analytical and responsive type tables.' } ], output: getManifestAsCode(V4_MANIFEST, [ @@ -1010,6 +1015,304 @@ ruleTester.run(TEST_NAME, tablePersonalizationRule, { } ]) }, + [] + ), + createInvalidTest( + { + name: 'V4 - object page - empty object, group is checked, group is undefined', + filename: V4_MANIFEST_PATH, + code: getManifestAsCode(V4_MANIFEST, [ + { + path: ['sap.ui5', 'dependencies', 'minUI5Version'], + value: '1.121.1' + }, + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'type' + ], + value: 'ResponsiveTable' + }, + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'personalization' + ], + value: {} + } + ]), + errors: [ + { + line: 146, + column: 21, + message: + 'In case of using an object, omitting a setting is treated as false. Currently column, filter, group, sort are disabled in the Products table.' + } + ], + output: getManifestAsCode(V4_MANIFEST, [ + { + path: ['sap.ui5', 'dependencies', 'minUI5Version'], + value: '1.121.1' + }, + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'type' + ], + value: 'ResponsiveTable' + }, + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'personalization' + ], + value: true + } + ]) + }, + [FACETSV4] + ), + createInvalidTest( + { + name: 'V4 - object page - undefined settings, group is not checked, group is false', + filename: V4_MANIFEST_PATH, + code: getManifestAsCode(V4_MANIFEST, [ + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'personalization' + ], + value: { + group: false, + filter: false + } + } + ]), + errors: [ + { + line: 145, + column: 21, + message: + 'In case of using an object, omitting a setting is treated as false. Currently column, sort are disabled in the Products table.' + }, + { + line: 147, + column: 23, + message: 'Data filtering should be enabled in the Products table.' + } + ], + output: getManifestAsCode(V4_MANIFEST, [ + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'personalization' + ], + value: true + } + ]) + }, + [FACETSV4] + ), + createInvalidTest( + { + name: 'V4 - object page - single undefined property, group is not checked', + filename: V4_MANIFEST_PATH, + code: getManifestAsCode(V4_MANIFEST, [ + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'personalization' + ], + value: { + group: false, + filter: false, + sort: true + } + } + ]), + errors: [ + { + line: 145, + column: 21, + message: + 'In case of using an object, omitting a setting is treated as false. Currently column is disabled in the Products table.' + }, + { + line: 147, + column: 23, + message: 'Data filtering should be enabled in the Products table.' + } + ], + output: getManifestAsCode(V4_MANIFEST, [ + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'personalization' + ], + value: true + } + ]) + }, + [FACETSV4] + ), + createInvalidTest( + { + name: 'V4 - object page - undefined settings, group is checked, group is false', + filename: V4_MANIFEST_PATH, + code: getManifestAsCode(V4_MANIFEST, [ + { + path: ['sap.ui5', 'dependencies', 'minUI5Version'], + value: '1.121.1' + }, + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'type' + ], + value: 'ResponsiveTable' + }, + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'personalization' + ], + value: { + column: true, + group: false + } + } + ]), + errors: [ + { + line: 146, + column: 21, + message: + 'In case of using an object, omitting a setting is treated as false. Currently filter, sort are disabled in the Products table.' + }, + { + line: 148, + column: 23, + message: + 'Data grouping should be enabled in the Products table. Grouping is available for analytical and responsive type tables.' + } + ], + output: getManifestAsCode(V4_MANIFEST, [ + { + path: ['sap.ui5', 'dependencies', 'minUI5Version'], + value: '1.121.1' + }, + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'type' + ], + value: 'ResponsiveTable' + }, + { + path: [ + 'sap.ui5', + 'routing', + 'targets', + 'IncidentsObjectPage', + 'options', + 'settings', + 'controlConfiguration', + 'incidentFlow/@com.sap.vocabularies.UI.v1.LineItem', + 'tableSettings', + 'personalization' + ], + value: true + } + ]) + }, [FACETSV4] ) ] diff --git a/packages/eslint-plugin-fiori-tools/test/rules/sap-width-including-column-header.test.ts b/packages/eslint-plugin-fiori-tools/test/rules/sap-width-including-column-header.test.ts index 0ad4c526c99..79e2b151b34 100644 --- a/packages/eslint-plugin-fiori-tools/test/rules/sap-width-including-column-header.test.ts +++ b/packages/eslint-plugin-fiori-tools/test/rules/sap-width-including-column-header.test.ts @@ -161,7 +161,8 @@ ruleTester.run(`${TEST_NAME} - XML`, widthIncludingColumnHeaderRule, { ]), errors: [ { - messageId: 'width-including-column-header-manifest', + message: + 'Small tables (< 6 columns) should use widthIncludingColumnHeader: true for improved calculation of the column width. Add it to the control configuration of the table.', line: 124, column: 19 } @@ -244,7 +245,8 @@ ruleTester.run(`${TEST_NAME} - XML`, widthIncludingColumnHeaderRule, { ]), errors: [ { - messageId: 'width-including-column-header-manifest', + message: + 'Small tables (< 6 columns) should use widthIncludingColumnHeader: true for improved calculation of the column width. Add it to the control configuration of the Products table.', line: 139, column: 13 } diff --git a/packages/eslint-plugin-fiori-tools/test/test-helper.ts b/packages/eslint-plugin-fiori-tools/test/test-helper.ts index 122a723db11..9af40278b15 100644 --- a/packages/eslint-plugin-fiori-tools/test/test-helper.ts +++ b/packages/eslint-plugin-fiori-tools/test/test-helper.ts @@ -39,7 +39,7 @@ export const V4_FACETS_ANNOTATIONS = ` - +