Skip to content

Commit

Permalink
chore(cdk): migrate tuiOverscroll (#7922)
Browse files Browse the repository at this point in the history
Co-authored-by: Dementii Kalkov <[email protected]>
  • Loading branch information
demkalkov and Dementii Kalkov authored Jun 24, 2024
1 parent ebd03c2 commit 0560e8e
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ export const MODULES_TO_REMOVE: RemovedModule[] = [
name: 'TuiFocusableModule',
moduleSpecifier: '@taiga-ui/cdk',
},
{
name: 'TuiOverscrollModule',
moduleSpecifier: '@taiga-ui/cdk',
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
migrateThumbnailCard,
migrateToggle,
} from './templates';
import {migrateOverscroll} from './templates/migrate-overscroll';

function getAction<T>({
action,
Expand Down Expand Up @@ -102,6 +103,7 @@ export function migrateTemplates(fileSystem: DevkitFileSystem, options: TuiSchem
migrateLabeled,
migrateProgressSegmented,
migrateThumbnailCard,
migrateOverscroll,
] as const;

const progressLog = setupProgressLogger({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type {UpdateRecorder} from '@angular-devkit/schematics';
import type {DevkitFileSystem} from 'ng-morph';
import type {ElementLocation} from 'parse5/dist/common/token';
import type {Element} from 'parse5/dist/tree-adapters/default';

import {findElementsWithDirective} from '../../../../utils/templates/elements';
import {
getTemplateFromTemplateResource,
getTemplateOffset,
} from '../../../../utils/templates/template-resource';
import type {TemplateResource} from '../../../interfaces';
import {removeAttrs} from '../utils/remove-attrs';

const overscrollAttrName = 'tuiOverscroll';
const overscrollAttrNameDict = {
[overscrollAttrName.toLowerCase()]: true,
[`[${overscrollAttrName.toLowerCase()}]`]: true,
};
export function migrateOverscroll({
resource,
recorder,
fileSystem,
}: {
fileSystem: DevkitFileSystem;
recorder: UpdateRecorder;
resource: TemplateResource;
}): void {
const template = getTemplateFromTemplateResource(resource, fileSystem);
const templateOffset = getTemplateOffset(resource);

const elements = findElementsWithDirective(template, overscrollAttrName).filter(
({sourceCodeLocation}) => !!sourceCodeLocation,
);

if (!elements.length) {
return;
}

elements.forEach(({attrs, sourceCodeLocation}: Element) => {
const attrsToRemove = attrs.filter(({name}) => overscrollAttrNameDict[name]);

removeAttrs(
attrsToRemove,
sourceCodeLocation as ElementLocation,
recorder,
templateOffset,
);
});

addTodo(recorder, elements[0].sourceCodeLocation as ElementLocation, templateOffset);
}

function addTodo(
recorder: UpdateRecorder,
sourceCodeLocation: ElementLocation,
templateOffset: number,
): void {
recorder.insertRight(
templateOffset + (sourceCodeLocation?.startTag?.startOffset ?? 0),
'<!-- Taiga migration TODO: use "overscroll-behavior" ccs property instead of "tuiOverscroll" directive -->\n',
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import {join} from 'node:path';

import {HostTree} from '@angular-devkit/schematics';
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
import type {TuiSchema} from '@taiga-ui/cdk/schematics/ng-add/schema';
import {
createProject,
createSourceFile,
resetActiveProject,
saveActiveProject,
setActiveProject,
} from 'ng-morph';

import {createAngularJson} from '../../../utils/create-angular-json';

const collectionPath = join(__dirname, '../../../migration.json');

const COMPONENT_BEFORE = `
import { TuiOverscrollModule } from "@taiga-ui/cdk";
@Component({
standalone: true,
templateUrl: './test.template.html',
imports: [TuiOverscrollModule]
})
export class Test {
}`;

const COMPONENT_AFTER = `
@Component({
standalone: true,
templateUrl: './test.template.html',
imports: []
})
export class Test {
}`;

const TEMPLATE_BEFORE = `
<div tuiOverscroll="none"></div>
<div tuiOverscroll="all" class="box"></div>
<div [tuiOverscroll]="'scroll'"></div>
<div
tuiOverscroll
>
</div>
`;

const TEMPLATE_AFTER = `
<!-- Taiga migration TODO: use "overscroll-behavior" ccs property instead of "tuiOverscroll" directive -->
<div></div>
<div class="box"></div>
<div></div>
<div
>
</div>
`;

describe('ng-update', () => {
let host: UnitTestTree;
let runner: SchematicTestRunner;

beforeEach(() => {
host = new UnitTestTree(new HostTree());
runner = new SchematicTestRunner('schematics', collectionPath);

setActiveProject(createProject(host));

createMainFiles();

saveActiveProject();
});

it('should migrate overscroll in template', async () => {
const tree = await runner.runSchematic(
'updateToV4',
{'skip-logs': process.env['TUI_CI'] === 'true'} as Partial<TuiSchema>,
host,
);

expect(tree.readContent('test/app/test.template.html')).toEqual(TEMPLATE_AFTER);
});

it('should remove overscroll references in ts files', async () => {
const tree = await runner.runSchematic(
'updateToV4',
{'skip-logs': process.env['TUI_CI'] === 'true'} as Partial<TuiSchema>,
host,
);

expect(tree.readContent('test/app/test.component.ts')).toEqual(COMPONENT_AFTER);
});

afterEach(() => {
resetActiveProject();
});
});

function createMainFiles(): void {
createSourceFile('test/app/test.component.ts', COMPONENT_BEFORE);

createSourceFile('test/app/test.template.html', TEMPLATE_BEFORE);

createAngularJson();
createSourceFile(
'package.json',
'{"dependencies": {"@angular/core": "~13.0.0", "@taiga-ui/addon-commerce": "~3.42.0"}}',
);
}
16 changes: 16 additions & 0 deletions projects/cdk/schematics/utils/templates/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ export function findElementsByTagNames(html: string, tagNames: string[]): Elemen
return findElementsInTemplateByFn(html, el => tagNames.includes(el.tagName));
}

/**
* Parses a HTML fragment and traverses all AST nodes in order find elements that
* include the specified directive as attribute or input.
*/
export function findElementsWithDirective(
html: string,
attributeName: string,
): Element[] {
const lowercasedAttrName = attributeName.toLowerCase();
const inputName = `[${lowercasedAttrName}]`;

return findElementsInTemplateByFn(html, el =>
el.attrs?.some(({name}) => name === lowercasedAttrName || name === inputName),
);
}

/**
* Parses a HTML fragment and traverses all AST nodes in order find elements that
* include the specified attribute.
Expand Down

0 comments on commit 0560e8e

Please sign in to comment.