This is page 9 of 10. Use http://codebase.md/push-based/angular-toolkit-mcp?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .aiignore ├── .cursor │ ├── flows │ │ ├── component-refactoring │ │ │ ├── 01-review-component.mdc │ │ │ ├── 02-refactor-component.mdc │ │ │ ├── 03-validate-component.mdc │ │ │ └── angular-20.md │ │ ├── ds-refactoring-flow │ │ │ ├── 01-find-violations.mdc │ │ │ ├── 01b-find-all-violations.mdc │ │ │ ├── 02-plan-refactoring.mdc │ │ │ ├── 02b-plan-refactoring-for-all-violations.mdc │ │ │ ├── 03-fix-violations.mdc │ │ │ ├── 03-non-viable-cases.mdc │ │ │ ├── 04-validate-changes.mdc │ │ │ ├── 05-prepare-report.mdc │ │ │ └── clean-global-styles.mdc │ │ └── README.md │ └── mcp.json.example ├── .github │ └── workflows │ └── ci.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── assets │ ├── entain-logo.png │ └── entain.png ├── CONTRIBUTING.MD ├── docs │ ├── architecture-internal-design.md │ ├── component-refactoring-flow.md │ ├── contracts.md │ ├── ds-refactoring-flow.md │ ├── getting-started.md │ ├── README.md │ ├── tools.md │ └── writing-custom-tools.md ├── eslint.config.mjs ├── jest.config.ts ├── jest.preset.mjs ├── LICENSE ├── nx.json ├── package-lock.json ├── package.json ├── packages │ ├── .gitkeep │ ├── angular-mcp │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── assets │ │ │ │ └── .gitkeep │ │ │ └── main.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── vitest.config.mts │ │ └── webpack.config.cjs │ ├── angular-mcp-server │ │ ├── eslint.config.mjs │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── angular-mcp-server.ts │ │ │ ├── prompts │ │ │ │ └── prompt-registry.ts │ │ │ ├── tools │ │ │ │ ├── ds │ │ │ │ │ ├── component │ │ │ │ │ │ ├── get-deprecated-css-classes.tool.ts │ │ │ │ │ │ ├── get-ds-component-data.tool.ts │ │ │ │ │ │ ├── list-ds-components.tool.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ ├── deprecated-css-helpers.ts │ │ │ │ │ │ ├── doc-helpers.ts │ │ │ │ │ │ ├── metadata-helpers.ts │ │ │ │ │ │ └── paths-helpers.ts │ │ │ │ │ ├── component-contract │ │ │ │ │ │ ├── builder │ │ │ │ │ │ │ ├── build-component-contract.tool.ts │ │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ │ ├── spec │ │ │ │ │ │ │ │ ├── css-match.spec.ts │ │ │ │ │ │ │ │ ├── dom-slots.extractor.spec.ts │ │ │ │ │ │ │ │ ├── element-helpers.spec.ts │ │ │ │ │ │ │ │ ├── inline-styles.collector.spec.ts │ │ │ │ │ │ │ │ ├── meta.generator.spec.ts │ │ │ │ │ │ │ │ ├── public-api.extractor.spec.ts │ │ │ │ │ │ │ │ ├── styles.collector.spec.ts │ │ │ │ │ │ │ │ └── typescript-analyzer.spec.ts │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ ├── build-contract.ts │ │ │ │ │ │ │ ├── css-match.ts │ │ │ │ │ │ │ ├── dom-slots.extractor.ts │ │ │ │ │ │ │ ├── element-helpers.ts │ │ │ │ │ │ │ ├── inline-styles.collector.ts │ │ │ │ │ │ │ ├── meta.generator.ts │ │ │ │ │ │ │ ├── public-api.extractor.ts │ │ │ │ │ │ │ ├── styles.collector.ts │ │ │ │ │ │ │ └── typescript-analyzer.ts │ │ │ │ │ │ ├── diff │ │ │ │ │ │ │ ├── diff-component-contract.tool.ts │ │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ │ └── schema.ts │ │ │ │ │ │ │ ├── spec │ │ │ │ │ │ │ │ ├── diff-utils.spec.ts │ │ │ │ │ │ │ │ └── dom-path-utils.spec.ts │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ ├── diff-utils.ts │ │ │ │ │ │ │ └── dom-path-utils.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── list │ │ │ │ │ │ │ ├── list-component-contracts.tool.ts │ │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ │ ├── spec │ │ │ │ │ │ │ │ └── contract-list-utils.spec.ts │ │ │ │ │ │ │ └── utils │ │ │ │ │ │ │ └── contract-list-utils.ts │ │ │ │ │ │ └── shared │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ ├── spec │ │ │ │ │ │ │ └── contract-file-ops.spec.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ └── contract-file-ops.ts │ │ │ │ │ ├── component-usage-graph │ │ │ │ │ │ ├── build-component-usage-graph.tool.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ ├── config.ts │ │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ ├── angular-parser.ts │ │ │ │ │ │ ├── component-helpers.ts │ │ │ │ │ │ ├── component-usage-graph-builder.ts │ │ │ │ │ │ ├── path-resolver.ts │ │ │ │ │ │ └── unified-ast-analyzer.ts │ │ │ │ │ ├── ds.tools.ts │ │ │ │ │ ├── project │ │ │ │ │ │ ├── get-project-dependencies.tool.ts │ │ │ │ │ │ ├── report-deprecated-css.tool.ts │ │ │ │ │ │ └── utils │ │ │ │ │ │ ├── dependencies-helpers.ts │ │ │ │ │ │ └── styles-report-helpers.ts │ │ │ │ │ ├── report-violations │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ │ └── types.ts │ │ │ │ │ │ ├── report-all-violations.tool.ts │ │ │ │ │ │ └── report-violations.tool.ts │ │ │ │ │ ├── shared │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── models │ │ │ │ │ │ │ ├── input-schemas.model.ts │ │ │ │ │ │ │ └── schema-helpers.ts │ │ │ │ │ │ ├── utils │ │ │ │ │ │ │ ├── component-validation.ts │ │ │ │ │ │ │ ├── cross-platform-path.ts │ │ │ │ │ │ │ ├── handler-helpers.ts │ │ │ │ │ │ │ ├── output.utils.ts │ │ │ │ │ │ │ └── regex-helpers.ts │ │ │ │ │ │ └── violation-analysis │ │ │ │ │ │ ├── base-analyzer.ts │ │ │ │ │ │ ├── coverage-analyzer.ts │ │ │ │ │ │ ├── formatters.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ └── tools.ts │ │ │ │ ├── schema.ts │ │ │ │ ├── tools.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ │ └── validation │ │ │ ├── angular-mcp-server-options.schema.ts │ │ │ ├── ds-components-file-loader.validation.ts │ │ │ ├── ds-components-file.validation.ts │ │ │ ├── ds-components.schema.ts │ │ │ └── file-existence.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.tsbuildinfo │ │ └── vitest.config.mts │ ├── minimal-repo │ │ └── packages │ │ ├── application │ │ │ ├── angular.json │ │ │ ├── code-pushup.config.ts │ │ │ ├── src │ │ │ │ ├── app │ │ │ │ │ ├── app.component.ts │ │ │ │ │ ├── app.config.ts │ │ │ │ │ ├── app.routes.ts │ │ │ │ │ ├── components │ │ │ │ │ │ ├── refactoring-tests │ │ │ │ │ │ │ ├── bad-alert-tooltip-input.component.ts │ │ │ │ │ │ │ ├── bad-alert.component.ts │ │ │ │ │ │ │ ├── bad-button-dropdown.component.ts │ │ │ │ │ │ │ ├── bad-document.component.ts │ │ │ │ │ │ │ ├── bad-global-this.component.ts │ │ │ │ │ │ │ ├── bad-mixed-external-assets.component.css │ │ │ │ │ │ │ ├── bad-mixed-external-assets.component.html │ │ │ │ │ │ │ ├── bad-mixed-external-assets.component.ts │ │ │ │ │ │ │ ├── bad-mixed-not-standalone.component.ts │ │ │ │ │ │ │ ├── bad-mixed.component.ts │ │ │ │ │ │ │ ├── bad-mixed.module.ts │ │ │ │ │ │ │ ├── bad-modal-progress.component.ts │ │ │ │ │ │ │ ├── bad-this-window-document.component.ts │ │ │ │ │ │ │ ├── bad-window.component.ts │ │ │ │ │ │ │ ├── complex-components │ │ │ │ │ │ │ │ ├── first-case │ │ │ │ │ │ │ │ │ ├── dashboard-demo.component.html │ │ │ │ │ │ │ │ │ ├── dashboard-demo.component.scss │ │ │ │ │ │ │ │ │ ├── dashboard-demo.component.ts │ │ │ │ │ │ │ │ │ ├── dashboard-header.component.html │ │ │ │ │ │ │ │ │ ├── dashboard-header.component.scss │ │ │ │ │ │ │ │ │ └── dashboard-header.component.ts │ │ │ │ │ │ │ │ ├── second-case │ │ │ │ │ │ │ │ │ ├── complex-badge-widget.component.scss │ │ │ │ │ │ │ │ │ ├── complex-badge-widget.component.ts │ │ │ │ │ │ │ │ │ └── complex-widget-demo.component.ts │ │ │ │ │ │ │ │ └── third-case │ │ │ │ │ │ │ │ ├── product-card.component.scss │ │ │ │ │ │ │ │ ├── product-card.component.ts │ │ │ │ │ │ │ │ └── product-showcase.component.ts │ │ │ │ │ │ │ ├── group-1 │ │ │ │ │ │ │ │ ├── bad-mixed-1.component.ts │ │ │ │ │ │ │ │ ├── bad-mixed-1.module.ts │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-1.component.css │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-1.component.html │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-1.component.ts │ │ │ │ │ │ │ │ └── bad-mixed-not-standalone-1.component.ts │ │ │ │ │ │ │ ├── group-2 │ │ │ │ │ │ │ │ ├── bad-mixed-2.component.ts │ │ │ │ │ │ │ │ ├── bad-mixed-2.module.ts │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-2.component.css │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-2.component.html │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-2.component.ts │ │ │ │ │ │ │ │ └── bad-mixed-not-standalone-2.component.ts │ │ │ │ │ │ │ ├── group-3 │ │ │ │ │ │ │ │ ├── bad-mixed-3.component.spec.ts │ │ │ │ │ │ │ │ ├── bad-mixed-3.component.ts │ │ │ │ │ │ │ │ ├── bad-mixed-3.module.ts │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-3.component.css │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-3.component.html │ │ │ │ │ │ │ │ ├── bad-mixed-external-assets-3.component.ts │ │ │ │ │ │ │ │ ├── bad-mixed-not-standalone-3.component.ts │ │ │ │ │ │ │ │ └── lazy-loader-3.component.ts │ │ │ │ │ │ │ └── group-4 │ │ │ │ │ │ │ ├── multi-violation-test.component.html │ │ │ │ │ │ │ ├── multi-violation-test.component.scss │ │ │ │ │ │ │ └── multi-violation-test.component.ts │ │ │ │ │ │ └── validation-tests │ │ │ │ │ │ ├── circular-dependency.component.ts │ │ │ │ │ │ ├── external-files-missing.component.ts │ │ │ │ │ │ ├── invalid-lifecycle.component.ts │ │ │ │ │ │ ├── invalid-pipe-usage.component.ts │ │ │ │ │ │ ├── invalid-template-syntax.component.ts │ │ │ │ │ │ ├── missing-imports.component.ts │ │ │ │ │ │ ├── missing-method.component.ts │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── standalone-module-conflict.component.ts │ │ │ │ │ │ ├── standalone-module-conflict.module.ts │ │ │ │ │ │ ├── template-reference-error.component.ts │ │ │ │ │ │ ├── type-mismatch.component.ts │ │ │ │ │ │ ├── valid.component.ts │ │ │ │ │ │ ├── wrong-decorator-usage.component.ts │ │ │ │ │ │ └── wrong-property-binding.component.ts │ │ │ │ │ └── styles │ │ │ │ │ ├── bad-global-styles.scss │ │ │ │ │ ├── base │ │ │ │ │ │ ├── _reset.scss │ │ │ │ │ │ └── base.scss │ │ │ │ │ ├── components │ │ │ │ │ │ └── components.scss │ │ │ │ │ ├── extended-deprecated-styles.scss │ │ │ │ │ ├── layout │ │ │ │ │ │ └── layout.scss │ │ │ │ │ ├── new-styles-1.scss │ │ │ │ │ ├── new-styles-10.scss │ │ │ │ │ ├── new-styles-2.scss │ │ │ │ │ ├── new-styles-3.scss │ │ │ │ │ ├── new-styles-4.scss │ │ │ │ │ ├── new-styles-5.scss │ │ │ │ │ ├── new-styles-6.scss │ │ │ │ │ ├── new-styles-7.scss │ │ │ │ │ ├── new-styles-8.scss │ │ │ │ │ ├── new-styles-9.scss │ │ │ │ │ ├── themes │ │ │ │ │ │ └── themes.scss │ │ │ │ │ └── utilities │ │ │ │ │ └── utilities.scss │ │ │ │ ├── index.html │ │ │ │ ├── main.ts │ │ │ │ └── styles.css │ │ │ ├── tsconfig.app.json │ │ │ ├── tsconfig.json │ │ │ └── tsconfig.spec.json │ │ └── design-system │ │ ├── component-options.mjs │ │ ├── storybook │ │ │ └── card │ │ │ └── card-tabs │ │ │ └── overview.mdx │ │ ├── storybook-host-app │ │ │ └── src │ │ │ └── components │ │ │ ├── badge │ │ │ │ ├── badge-tabs │ │ │ │ │ ├── api.mdx │ │ │ │ │ ├── examples.mdx │ │ │ │ │ └── overview.mdx │ │ │ │ ├── badge.component.mdx │ │ │ │ └── badge.component.stories.ts │ │ │ ├── modal │ │ │ │ ├── demo-cdk-dialog-cmp.component.ts │ │ │ │ ├── demo-modal-cmp.component.ts │ │ │ │ ├── modal-tabs │ │ │ │ │ ├── api.mdx │ │ │ │ │ ├── examples.mdx │ │ │ │ │ └── overview.mdx │ │ │ │ ├── modal.component.mdx │ │ │ │ └── modal.component.stories.ts │ │ │ └── segmented-control │ │ │ ├── segmented-control-tabs │ │ │ │ ├── api.mdx │ │ │ │ ├── examples.mdx │ │ │ │ └── overview.mdx │ │ │ ├── segmented-control.component.mdx │ │ │ └── segmented-control.component.stories.ts │ │ └── ui │ │ ├── badge │ │ │ ├── package.json │ │ │ ├── project.json │ │ │ └── src │ │ │ └── badge.component.ts │ │ ├── modal │ │ │ ├── package.json │ │ │ ├── project.json │ │ │ └── src │ │ │ ├── modal-content.component.ts │ │ │ ├── modal-header │ │ │ │ └── modal-header.component.ts │ │ │ ├── modal-header-drag │ │ │ │ └── modal-header-drag.component.ts │ │ │ └── modal.component.ts │ │ ├── rx-host-listener │ │ │ ├── package.json │ │ │ ├── project.json │ │ │ └── src │ │ │ └── rx-host-listener.ts │ │ └── segmented-control │ │ ├── package.json │ │ ├── project.json │ │ └── src │ │ ├── segmented-control.component.html │ │ ├── segmented-control.component.ts │ │ ├── segmented-control.token.ts │ │ └── segmented-option.component.ts │ └── shared │ ├── angular-ast-utils │ │ ├── .spec.swcrc │ │ ├── ai │ │ │ ├── API.md │ │ │ ├── EXAMPLES.md │ │ │ └── FUNCTIONS.md │ │ ├── docs │ │ │ └── angular-component-tree.md │ │ ├── eslint.config.mjs │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── constants.ts │ │ │ ├── decorator-config.visitor.inline-styles.spec.ts │ │ │ ├── decorator-config.visitor.spec.ts │ │ │ ├── decorator-config.visitor.ts │ │ │ ├── parse-component.ts │ │ │ ├── schema.ts │ │ │ ├── styles │ │ │ │ └── utils.ts │ │ │ ├── template │ │ │ │ ├── noop-tmpl-visitor.ts │ │ │ │ ├── template.walk.ts │ │ │ │ ├── utils.spec.ts │ │ │ │ ├── utils.ts │ │ │ │ └── utils.unit.test.ts │ │ │ ├── ts.walk.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ └── vitest.config.mts │ ├── DEPENDENCIES.md │ ├── ds-component-coverage │ │ ├── .spec.swcrc │ │ ├── ai │ │ │ ├── API.md │ │ │ ├── EXAMPLES.md │ │ │ └── FUNCTIONS.md │ │ ├── docs │ │ │ ├── examples │ │ │ │ ├── report.json │ │ │ │ └── report.md │ │ │ ├── images │ │ │ │ └── report-overview.png │ │ │ └── README.md │ │ ├── jest.config.ts │ │ ├── mocks │ │ │ └── fixtures │ │ │ └── e2e │ │ │ ├── asset-location │ │ │ │ ├── code-pushup.config.ts │ │ │ │ ├── inl-styl-inl-tmpl │ │ │ │ │ └── inl-styl-inl-tmpl.component.ts │ │ │ │ ├── inl-styl-url-tmpl │ │ │ │ │ ├── inl-styl-url-tmpl.component.html │ │ │ │ │ └── inl-styl-url-tmpl.component.ts │ │ │ │ ├── multi-url-styl-inl-tmpl │ │ │ │ │ ├── multi-url-styl-inl-tmpl-1.component.css │ │ │ │ │ ├── multi-url-styl-inl-tmpl-2.component.css │ │ │ │ │ └── multi-url-styl-inl-tmpl.component.ts │ │ │ │ ├── url-styl-inl-tmpl │ │ │ │ │ ├── url-styl-inl-tmpl.component.css │ │ │ │ │ └── url-styl-inl-tmpl.component.ts │ │ │ │ ├── url-styl-single-inl-tmpl │ │ │ │ │ ├── url-styl-inl-tmpl.component.ts │ │ │ │ │ └── url-styl-single-inl-tmpl.component.css │ │ │ │ └── url-styl-url-tmpl │ │ │ │ ├── inl-styl-url-tmpl.component.css │ │ │ │ ├── inl-styl-url-tmpl.component.html │ │ │ │ └── inl-styl-url-tmpl.component.ts │ │ │ ├── demo │ │ │ │ ├── code-pushup.config.ts │ │ │ │ ├── prompt.md │ │ │ │ └── src │ │ │ │ ├── bad-button-dropdown.component.ts │ │ │ │ ├── bad-modal-progress.component.ts │ │ │ │ ├── mixed-external-assets.component.css │ │ │ │ ├── mixed-external-assets.component.html │ │ │ │ ├── mixed-external-assets.component.ts │ │ │ │ └── sub-folder-1 │ │ │ │ ├── bad-alert.component.ts │ │ │ │ ├── button.component.ts │ │ │ │ └── sub-folder-2 │ │ │ │ ├── bad-alert-tooltip-input.component.ts │ │ │ │ └── bad-mixed.component.ts │ │ │ ├── line-number │ │ │ │ ├── code-pushup.config.ts │ │ │ │ ├── inl-styl-single.component.ts │ │ │ │ ├── inl-styl-span.component.ts │ │ │ │ ├── inl-tmpl-single.component.ts │ │ │ │ ├── inl-tmpl-span.component.ts │ │ │ │ ├── url-style │ │ │ │ │ ├── url-styl-single.component.css │ │ │ │ │ ├── url-styl-single.component.ts │ │ │ │ │ ├── url-styl-span.component.css │ │ │ │ │ └── url-styl-span.component.ts │ │ │ │ └── url-tmpl │ │ │ │ ├── url-tmpl-single.component.html │ │ │ │ ├── url-tmpl-single.component.ts │ │ │ │ ├── url-tmpl-span.component.html │ │ │ │ └── url-tmpl-span.component.ts │ │ │ ├── style-format │ │ │ │ ├── code-pushup.config.ts │ │ │ │ ├── inl-css.component.ts │ │ │ │ ├── inl-scss.component.ts │ │ │ │ ├── styles.css │ │ │ │ ├── styles.scss │ │ │ │ ├── url-css.component.ts │ │ │ │ └── url-scss.component.ts │ │ │ └── template-syntax │ │ │ ├── class-attribute.component.ts │ │ │ ├── class-binding.component.ts │ │ │ ├── code-pushup.config.ts │ │ │ └── ng-class-binding.component.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── core.config.ts │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── constants.ts │ │ │ ├── ds-component-coverage.plugin.ts │ │ │ ├── runner │ │ │ │ ├── audits │ │ │ │ │ └── ds-coverage │ │ │ │ │ ├── class-definition.utils.ts │ │ │ │ │ ├── class-definition.visitor.ts │ │ │ │ │ ├── class-definition.visitor.unit.test.ts │ │ │ │ │ ├── class-usage.utils.ts │ │ │ │ │ ├── class-usage.visitor.spec.ts │ │ │ │ │ ├── class-usage.visitor.ts │ │ │ │ │ ├── constants.ts │ │ │ │ │ ├── ds-coverage.audit.ts │ │ │ │ │ ├── schema.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── create-runner.ts │ │ │ │ └── schema.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ └── vitest.config.mts │ ├── LLMS.md │ ├── models │ │ ├── ai │ │ │ ├── API.md │ │ │ ├── EXAMPLES.md │ │ │ └── FUNCTIONS.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── cli.ts │ │ │ ├── diagnostics.ts │ │ │ └── mcp.ts │ │ ├── tsconfig.json │ │ └── tsconfig.lib.json │ ├── styles-ast-utils │ │ ├── .spec.swcrc │ │ ├── ai │ │ │ ├── API.md │ │ │ ├── EXAMPLES.md │ │ │ └── FUNCTIONS.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── postcss-safe-parser.d.ts │ │ │ ├── styles-ast-utils.spec.ts │ │ │ ├── styles-ast-utils.ts │ │ │ ├── stylesheet.parse.ts │ │ │ ├── stylesheet.parse.unit.test.ts │ │ │ ├── stylesheet.visitor.ts │ │ │ ├── stylesheet.walk.ts │ │ │ ├── types.ts │ │ │ ├── utils.ts │ │ │ └── utils.unit.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ └── vitest.config.mts │ ├── typescript-ast-utils │ │ ├── .spec.swcrc │ │ ├── ai │ │ │ ├── API.md │ │ │ ├── EXAMPLES.md │ │ │ └── FUNCTIONS.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── constants.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ └── vitest.config.mts │ └── utils │ ├── .spec.swcrc │ ├── ai │ │ ├── API.md │ │ ├── EXAMPLES.md │ │ └── FUNCTIONS.md │ ├── package.json │ ├── README.md │ ├── src │ │ ├── index.ts │ │ └── lib │ │ ├── execute-process.ts │ │ ├── execute-process.unit.test.ts │ │ ├── file │ │ │ ├── default-export-loader.spec.ts │ │ │ ├── default-export-loader.ts │ │ │ ├── file.resolver.ts │ │ │ └── find-in-file.ts │ │ ├── format-command-log.integration.test.ts │ │ ├── format-command-log.ts │ │ ├── logging.ts │ │ └── utils.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ ├── vite.config.ts │ └── vitest.config.mts ├── README.md ├── testing │ ├── setup │ │ ├── eslint.config.mjs │ │ ├── eslint.next.config.mjs │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.d.ts │ │ │ ├── index.mjs │ │ │ └── memfs.constants.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ ├── vitest.config.mts │ │ └── vitest.integration.config.mts │ ├── utils │ │ ├── eslint.config.mjs │ │ ├── eslint.next.config.mjs │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.ts │ │ │ └── lib │ │ │ ├── constants.ts │ │ │ ├── e2e-setup.ts │ │ │ ├── execute-process-helper.mock.ts │ │ │ ├── execute-process.mock.mjs │ │ │ ├── os-agnostic-paths.ts │ │ │ ├── os-agnostic-paths.unit.test.ts │ │ │ ├── source-file-from.code.ts │ │ │ └── string.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.lib.json │ │ ├── tsconfig.spec.json │ │ ├── vite.config.ts │ │ ├── vitest.config.mts │ │ └── vitest.integration.config.mts │ └── vitest-setup │ ├── eslint.config.mjs │ ├── eslint.next.config.mjs │ ├── package.json │ ├── README.md │ ├── src │ │ ├── index.ts │ │ └── lib │ │ ├── configuration.ts │ │ └── fs-memfs.setup-file.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ ├── vite.config.ts │ ├── vitest.config.mts │ └── vitest.integration.config.mts ├── tools │ ├── nx-advanced-profile.bin.js │ ├── nx-advanced-profile.js │ ├── nx-advanced-profile.postinstall.js │ └── perf_hooks.patch.js ├── tsconfig.base.json ├── tsconfig.json └── vitest.workspace.ts ``` # Files -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/src/lib/runner/audits/ds-coverage/class-usage.visitor.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { ClassUsageVisitor } from './class-usage.visitor'; 2 | import type { 3 | ParsedTemplate, 4 | ParseTemplateOptions, 5 | } from '@angular/compiler' with { 'resolution-mode': 'import' }; 6 | 7 | describe('ClassCollectorVisitor', () => { 8 | let visitor: ClassUsageVisitor; 9 | let parseTemplate: ( 10 | template: string, 11 | templateUrl: string, 12 | options?: ParseTemplateOptions, 13 | ) => ParsedTemplate; 14 | 15 | beforeAll(async () => { 16 | parseTemplate = (await import('@angular/compiler')).parseTemplate; 17 | }); 18 | beforeEach(() => { 19 | visitor = new ClassUsageVisitor({ 20 | componentName: 'CounterComponent', 21 | deprecatedCssClasses: ['count', 'count-badge', 'count-item'], 22 | docsUrl: 'my.doc#CounterComponent', 23 | }); 24 | }); 25 | 26 | it('should not find class when it is not a class-binding', () => { 27 | const template = ` 28 | <ms-list-item 29 | [count]="link.count" 30 | > 31 | </ms-list-item> 32 | `; 33 | 34 | const ast = parseTemplate(template, 'template.html'); 35 | 36 | ast.nodes.forEach((node) => node.visit(visitor)); 37 | 38 | expect(visitor.getIssues()).toHaveLength(0); 39 | }); 40 | 41 | it('<div class="count">1</div> should find node with css class', () => { 42 | const template = `<div class="count">1</div>`; 43 | 44 | const ast = parseTemplate(template, 'template.html'); 45 | ast.nodes.forEach((node) => node.visit(visitor)); 46 | 47 | expect(visitor.getIssues()).toStrictEqual([ 48 | expect.objectContaining({ 49 | message: expect.stringContaining('CounterComponent'), 50 | }), 51 | ]); 52 | }); 53 | 54 | it('<div class="count">1</div> should find node within other css classes', () => { 55 | const template = `<div class="a count b">1</div>`; 56 | 57 | const ast = parseTemplate(template, 'template.html'); 58 | ast.nodes.forEach((node) => node.visit(visitor)); 59 | 60 | expect(visitor.getIssues()).toStrictEqual([ 61 | expect.objectContaining({ 62 | message: expect.stringContaining('CounterComponent'), 63 | severity: 'error', 64 | source: expect.objectContaining({ 65 | file: 'template.html', 66 | position: expect.any(Object), 67 | }), 68 | }), 69 | ]); 70 | }); 71 | 72 | it('<div [class.count]="true">2</div>', () => { 73 | const template = `<div [class.count]="true">2</div>`; 74 | 75 | const ast = parseTemplate(template, 'template.html'); 76 | ast.nodes.forEach((node) => node.visit(visitor)); 77 | 78 | expect(visitor.getIssues()).toStrictEqual([ 79 | expect.objectContaining({ 80 | message: expect.stringContaining('CounterComponent'), 81 | severity: 'error', 82 | source: expect.objectContaining({ 83 | file: 'template.html', 84 | position: expect.any(Object), 85 | }), 86 | }), 87 | ]); 88 | }); 89 | 90 | it('<div [class.count]="false">2</div> should find node with class-binding', () => { 91 | const template = `<div [class.count]="false">2</div>`; 92 | 93 | const ast = parseTemplate(template, 'template.html'); 94 | ast.nodes.forEach((node) => node.visit(visitor)); 95 | 96 | expect(visitor.getIssues()).toStrictEqual([ 97 | expect.objectContaining({ 98 | message: expect.stringContaining('CounterComponent'), 99 | severity: 'error', 100 | source: expect.objectContaining({ 101 | file: 'template.html', 102 | position: expect.any(Object), 103 | }), 104 | }), 105 | ]); 106 | }); 107 | 108 | it('<div [class.a]="true">3</div> should not find not when other class is used in class-binding', () => { 109 | const template = `<div [class.a]="true">3</div>`; 110 | 111 | const ast = parseTemplate(template, 'template.html'); 112 | ast.nodes.forEach((node) => node.visit(visitor)); 113 | 114 | expect(visitor.getIssues()).toHaveLength(0); 115 | }); 116 | 117 | it('<div [class.a]="false">3</div> should not find node when other class is used in class-binding', () => { 118 | const template = `<div [class.a]="false">3</div>`; 119 | 120 | const ast = parseTemplate(template, 'template.html'); 121 | ast.nodes.forEach((node) => node.visit(visitor)); 122 | 123 | expect(visitor.getIssues()).toHaveLength(0); 124 | }); 125 | 126 | it("<div [ngClass]=\"['count', 'second']\">5</div> should find node when class is used in ngClass-binding", () => { 127 | const template = `<div [ngClass]="['count', 'second']">5</div>`; 128 | 129 | const ast = parseTemplate(template, 'template.html'); 130 | ast.nodes.forEach((node) => node.visit(visitor)); 131 | 132 | expect(visitor.getIssues()).toStrictEqual([ 133 | expect.objectContaining({ 134 | message: expect.stringContaining('CounterComponent'), 135 | severity: 'error', 136 | source: expect.objectContaining({ 137 | file: 'template.html', 138 | position: expect.any(Object), 139 | }), 140 | }), 141 | ]); 142 | }); 143 | 144 | it('<div [ngClass]="{ count: true, second: true, third: true }">6</div> should find node when class is used in ngClass-binding with object-binding', () => { 145 | const template = `<div [ngClass]="{ count: true, second: true, third: true }">6</div>`; 146 | 147 | const ast = parseTemplate(template, 'template.html'); 148 | ast.nodes.forEach((node) => node.visit(visitor)); 149 | 150 | expect(visitor.getIssues()).toStrictEqual([ 151 | expect.objectContaining({ 152 | message: expect.stringContaining('CounterComponent'), 153 | severity: 'error', 154 | source: expect.objectContaining({ 155 | file: 'template.html', 156 | position: expect.any(Object), 157 | }), 158 | }), 159 | ]); 160 | }); 161 | 162 | it('<div [ngClass]="{ count: false, second: true, third: true }">6</div> should find node when class is used in ngClass-binding with object-binding and other classes', () => { 163 | const template = `<div [ngClass]="{ count: false, second: true, third: true }">6</div>`; 164 | 165 | const ast = parseTemplate(template, 'template.html'); 166 | ast.nodes.forEach((node) => node.visit(visitor)); 167 | 168 | expect(visitor.getIssues()).toStrictEqual([ 169 | expect.objectContaining({ 170 | message: expect.stringContaining('CounterComponent'), 171 | severity: 'error', 172 | source: expect.objectContaining({ 173 | file: 'template.html', 174 | position: expect.any(Object), 175 | }), 176 | }), 177 | ]); 178 | }); 179 | 180 | it('<div [ngClass]="{ \'count second\': true }">7</div> should find node when class is used in ngClass-binding with object-binding and condensed signature', () => { 181 | const template = `<div [ngClass]="{ 'count second': true }">7</div>`; 182 | 183 | const ast = parseTemplate(template, 'template.html'); 184 | ast.nodes.forEach((node) => node.visit(visitor)); 185 | 186 | expect(visitor.getIssues()).toStrictEqual([ 187 | expect.objectContaining({ 188 | message: expect.stringContaining('CounterComponent'), 189 | severity: 'error', 190 | source: expect.objectContaining({ 191 | file: 'template.html', 192 | position: expect.any(Object), 193 | }), 194 | }), 195 | ]); 196 | }); 197 | 198 | it('<div [ngClass]="{ \'count second\': false }">7</div>', () => { 199 | const template = `<div [ngClass]="{ 'count second': false }">7</div>`; 200 | 201 | const ast = parseTemplate(template, 'template.html'); 202 | ast.nodes.forEach((node) => node.visit(visitor)); 203 | 204 | expect(visitor.getIssues()).toStrictEqual([ 205 | expect.objectContaining({ 206 | message: expect.stringContaining('CounterComponent'), 207 | severity: 'error', 208 | source: expect.objectContaining({ 209 | file: 'template.html', 210 | position: expect.any(Object), 211 | }), 212 | }), 213 | ]); 214 | }); 215 | 216 | it('should find all nodes in @if-blocks', () => { 217 | const template = ` 218 | @if (true){ 219 | <div id="1" class="count"></div> 220 | } 221 | <div> 222 | <span> 223 | @if (true){ 224 | <div id="2" class="count"></div> 225 | } 226 | </span> 227 | </div> 228 | `; 229 | 230 | const ast = parseTemplate(template, 'template.html'); 231 | ast.nodes.forEach((node) => node.visit(visitor)); 232 | 233 | expect(visitor.getIssues()).toHaveLength(2); 234 | expect(visitor.getIssues()).toEqual([ 235 | expect.objectContaining({ 236 | message: expect.stringContaining('CounterComponent'), 237 | severity: 'error', 238 | source: expect.objectContaining({ 239 | file: 'template.html', 240 | position: expect.any(Object), 241 | }), 242 | }), 243 | expect.objectContaining({ 244 | message: expect.stringContaining('CounterComponent'), 245 | severity: 'error', 246 | source: expect.objectContaining({ 247 | file: 'template.html', 248 | position: expect.any(Object), 249 | }), 250 | }), 251 | ]); 252 | }); 253 | 254 | it('should find all nodes with *ngIf', () => { 255 | const template = ` 256 | <ng-container *ngIf="true"> 257 | <div id="1" class="count"></div> 258 | </ng-container> 259 | <div> 260 | <span> 261 | 262 | <div *ngIf="true" id="2" class="count"></div> 263 | 264 | </span> 265 | </div> 266 | `; 267 | 268 | const ast = parseTemplate(template, 'template.html'); 269 | ast.nodes.forEach((node) => node.visit(visitor)); 270 | 271 | expect(visitor.getIssues()).toHaveLength(2); 272 | expect(visitor.getIssues()).toEqual([ 273 | expect.objectContaining({ 274 | message: expect.stringContaining('CounterComponent'), 275 | severity: 'error', 276 | source: expect.objectContaining({ 277 | file: 'template.html', 278 | position: expect.any(Object), 279 | }), 280 | }), 281 | expect.objectContaining({ 282 | message: expect.stringContaining('CounterComponent'), 283 | severity: 'error', 284 | source: expect.objectContaining({ 285 | file: 'template.html', 286 | position: expect.any(Object), 287 | }), 288 | }), 289 | ]); 290 | }); 291 | 292 | it('should find all nodes in @for-block', () => { 293 | const template = ` 294 | @for (item of items; track item.name) { 295 | <div id="1" class="count"></div> 296 | } 297 | <div> 298 | <span> 299 | @for (item of items; track item.name) { 300 | <div id="2" class="count"></div> 301 | } 302 | </span> 303 | </div> 304 | `; 305 | 306 | const ast = parseTemplate(template, 'template.html'); 307 | ast.nodes.forEach((node) => node.visit(visitor)); 308 | 309 | expect(visitor.getIssues()).toHaveLength(2); 310 | expect(visitor.getIssues()).toEqual([ 311 | expect.objectContaining({ 312 | message: expect.stringContaining('CounterComponent'), 313 | severity: 'error', 314 | source: expect.objectContaining({ 315 | file: 'template.html', 316 | position: expect.any(Object), 317 | }), 318 | }), 319 | expect.objectContaining({ 320 | message: expect.stringContaining('CounterComponent'), 321 | severity: 'error', 322 | source: expect.objectContaining({ 323 | file: 'template.html', 324 | position: expect.any(Object), 325 | }), 326 | }), 327 | ]); 328 | }); 329 | 330 | it('should find all nodes with *ngFor', () => { 331 | const template = ` 332 | <div id="1" *ngFor="let item of [1,2,3]" class="count"></div> 333 | `; 334 | 335 | const ast = parseTemplate(template, 'template.html'); 336 | ast.nodes.forEach((node) => node.visit(visitor)); 337 | 338 | expect(visitor.getIssues()).toStrictEqual([ 339 | expect.objectContaining({ 340 | message: expect.stringContaining('CounterComponent'), 341 | severity: 'error', 342 | source: expect.objectContaining({ 343 | file: 'template.html', 344 | position: expect.any(Object), 345 | }), 346 | }), 347 | ]); 348 | }); 349 | 350 | it('should find all nodes with *switch', () => { 351 | const template = ` 352 | <ng-container *ngSwitchCase="userPermissions"> 353 | <ng-container *ngSwitchCase="'admin'"> 354 | <div id="1" class="count"></div> 355 | </ng-container> 356 | <ng-container *ngSwitchCase="'reviewer'"> 357 | 358 | </ng-container> 359 | <ng-container *ngSwitchDefault> 360 | 361 | </ng-container> 362 | </ng-container> 363 | `; 364 | 365 | const ast = parseTemplate(template, 'template.html'); 366 | ast.nodes.forEach((node) => node.visit(visitor)); 367 | 368 | expect(visitor.getIssues()).toStrictEqual([ 369 | expect.objectContaining({ 370 | message: expect.stringContaining('CounterComponent'), 371 | severity: 'error', 372 | source: expect.objectContaining({ 373 | file: 'template.html', 374 | position: expect.any(Object), 375 | }), 376 | }), 377 | ]); 378 | }); 379 | 380 | it('should find all nodes inside @switch', () => { 381 | const template = ` 382 | @switch (userPermissions) { 383 | @case ('admin') { 384 | <div id="1" class="count"></div> 385 | } 386 | @case ('reviewer') { 387 | 388 | } 389 | @case ('editor') { 390 | 391 | } 392 | @default { 393 | 394 | } 395 | } 396 | `; 397 | 398 | const ast = parseTemplate(template, 'template.html'); 399 | ast.nodes.forEach((node) => node.visit(visitor)); 400 | 401 | expect(visitor.getIssues()).toStrictEqual([ 402 | expect.objectContaining({ 403 | message: expect.stringContaining('CounterComponent'), 404 | severity: 'error', 405 | source: expect.objectContaining({ 406 | file: 'template.html', 407 | position: expect.any(Object), 408 | }), 409 | }), 410 | ]); 411 | }); 412 | 413 | it('should find all nodes inside @defer', () => { 414 | const template = ` 415 | @defer { 416 | <div id="1" class="count"></div> 417 | } 418 | `; 419 | 420 | const ast = parseTemplate(template, 'template.html'); 421 | ast.nodes.forEach((node) => node.visit(visitor)); 422 | 423 | expect(visitor.getIssues()).toStrictEqual([ 424 | expect.objectContaining({ 425 | message: expect.stringContaining('CounterComponent'), 426 | severity: 'error', 427 | source: expect.objectContaining({ 428 | file: 'template.html', 429 | position: expect.any(Object), 430 | }), 431 | }), 432 | ]); 433 | }); 434 | 435 | it('should find deprecated classes in interpolated class attributes', () => { 436 | const template = `<div class="count count-{{ size() }} other-class">Content</div>`; 437 | 438 | const ast = parseTemplate(template, 'template.html'); 439 | ast.nodes.forEach((node) => node.visit(visitor)); 440 | 441 | expect(visitor.getIssues()).toStrictEqual([ 442 | expect.objectContaining({ 443 | message: expect.stringContaining('CounterComponent'), 444 | severity: 'error', 445 | source: expect.objectContaining({ 446 | file: 'template.html', 447 | position: expect.any(Object), 448 | }), 449 | }), 450 | ]); 451 | }); 452 | 453 | // Deduplication tests 454 | describe('deduplication', () => { 455 | it('should deduplicate multiple deprecated classes in same class attribute', () => { 456 | const template = `<div class="count count-badge other-class">Content</div>`; 457 | 458 | const ast = parseTemplate(template, 'template.html'); 459 | ast.nodes.forEach((node) => node.visit(visitor)); 460 | 461 | expect(visitor.getIssues()).toHaveLength(1); 462 | const message = visitor.getIssues()[0].message; 463 | expect(message).toContain('count, count-badge'); 464 | expect(message).toContain('CounterComponent'); 465 | expect(visitor.getIssues()[0]).toEqual( 466 | expect.objectContaining({ 467 | severity: 'error', 468 | source: expect.objectContaining({ 469 | file: 'template.html', 470 | position: expect.any(Object), 471 | }), 472 | }), 473 | ); 474 | }); 475 | 476 | it('should deduplicate multiple deprecated classes in ngClass array', () => { 477 | const template = `<div [ngClass]="['count', 'count-badge', 'other-class']">Content</div>`; 478 | 479 | const ast = parseTemplate(template, 'template.html'); 480 | ast.nodes.forEach((node) => node.visit(visitor)); 481 | 482 | expect(visitor.getIssues()).toHaveLength(1); 483 | const message = visitor.getIssues()[0].message; 484 | expect(message).toContain('count, count-badge'); 485 | expect(message).toContain('CounterComponent'); 486 | expect(visitor.getIssues()[0]).toEqual( 487 | expect.objectContaining({ 488 | severity: 'error', 489 | source: expect.objectContaining({ 490 | file: 'template.html', 491 | position: expect.any(Object), 492 | }), 493 | }), 494 | ); 495 | }); 496 | 497 | it('should deduplicate multiple deprecated classes in ngClass object', () => { 498 | const template = `<div [ngClass]="{ count: true, 'count-badge': true, other: false }">Content</div>`; 499 | 500 | const ast = parseTemplate(template, 'template.html'); 501 | ast.nodes.forEach((node) => node.visit(visitor)); 502 | 503 | expect(visitor.getIssues()).toHaveLength(1); 504 | const message = visitor.getIssues()[0].message; 505 | expect(message).toContain('count, count-badge'); 506 | expect(message).toContain('CounterComponent'); 507 | expect(visitor.getIssues()[0]).toEqual( 508 | expect.objectContaining({ 509 | severity: 'error', 510 | source: expect.objectContaining({ 511 | file: 'template.html', 512 | position: expect.any(Object), 513 | }), 514 | }), 515 | ); 516 | }); 517 | 518 | it('should still create single issue for single deprecated class', () => { 519 | const template = `<div class="count other-class">Content</div>`; 520 | 521 | const ast = parseTemplate(template, 'template.html'); 522 | ast.nodes.forEach((node) => node.visit(visitor)); 523 | 524 | expect(visitor.getIssues()).toHaveLength(1); 525 | const message = visitor.getIssues()[0].message; 526 | expect(message).toContain('count'); 527 | expect(message).not.toContain(','); 528 | expect(message).toContain('CounterComponent'); 529 | expect(visitor.getIssues()[0]).toEqual( 530 | expect.objectContaining({ 531 | severity: 'error', 532 | source: expect.objectContaining({ 533 | file: 'template.html', 534 | position: expect.any(Object), 535 | }), 536 | }), 537 | ); 538 | }); 539 | 540 | it('should deduplicate three deprecated classes in same class attribute', () => { 541 | const template = `<div class="count count-badge count-item other-class">Content</div>`; 542 | 543 | const ast = parseTemplate(template, 'template.html'); 544 | ast.nodes.forEach((node) => node.visit(visitor)); 545 | 546 | expect(visitor.getIssues()).toHaveLength(1); 547 | const message = visitor.getIssues()[0].message; 548 | expect(message).toContain('count, count-badge, count-item'); 549 | expect(message).toContain('CounterComponent'); 550 | expect(visitor.getIssues()[0]).toEqual( 551 | expect.objectContaining({ 552 | severity: 'error', 553 | source: expect.objectContaining({ 554 | file: 'template.html', 555 | position: expect.any(Object), 556 | }), 557 | }), 558 | ); 559 | }); 560 | }); 561 | }); 562 | ``` -------------------------------------------------------------------------------- /packages/shared/angular-ast-utils/src/lib/template/utils.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { 2 | extractClassNamesFromNgClassAST, 3 | ngClassContainsClass, 4 | ngClassesIncludeClassName, 5 | } from './utils'; 6 | import type { 7 | ASTWithSource, 8 | TmplAstElement, 9 | } from '@angular/compiler' with { 'resolution-mode': 'import' }; 10 | 11 | let parseTemplate: typeof import('@angular/compiler').parseTemplate; 12 | 13 | beforeAll(async () => { 14 | parseTemplate = (await import('@angular/compiler')).parseTemplate; 15 | }); 16 | 17 | function parseNgClassExpression(expression: string): ASTWithSource { 18 | const template = `<div [ngClass]="${expression}"></div>`; 19 | const result = parseTemplate(template, 'test.html'); 20 | const element = result.nodes[0] as TmplAstElement; 21 | const ngClassInput = element.inputs.find((input) => input.name === 'ngClass'); 22 | return ngClassInput?.value as ASTWithSource; 23 | } 24 | 25 | describe('extractClassNamesFromNgClassAST', () => { 26 | it('should extract classes from array literals', () => { 27 | const expression = "['btn', 'btn-primary', someVariable]"; 28 | const astWithSource = parseNgClassExpression(expression); 29 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 30 | 'btn', 31 | 'btn-primary', 32 | 'other', 33 | ]); 34 | 35 | expect(result).toEqual(['btn', 'btn-primary']); 36 | }); 37 | 38 | it('should extract classes from object literals', () => { 39 | const expression = 40 | "{ 'btn': true, 'btn-primary': isActive, 'other-class': false }"; 41 | const astWithSource = parseNgClassExpression(expression); 42 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 43 | 'btn', 44 | 'btn-primary', 45 | 'other-class', 46 | ]); 47 | 48 | expect(result).toEqual(['btn', 'btn-primary', 'other-class']); 49 | }); 50 | 51 | it('should extract classes from string literals', () => { 52 | const expression = "'btn btn-primary active'"; 53 | const astWithSource = parseNgClassExpression(expression); 54 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 55 | 'btn', 56 | 'btn-primary', 57 | 'active', 58 | ]); 59 | 60 | expect(result).toEqual(['btn', 'btn-primary', 'active']); 61 | }); 62 | 63 | it('should extract classes from ternary expressions without false positives from conditions', () => { 64 | const expression = 65 | "option?.logo?.toLowerCase() === 'card' ? 'card-special' : 'card-normal'"; 66 | const astWithSource = parseNgClassExpression(expression); 67 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 68 | 'card', 69 | 'card-special', 70 | 'card-normal', 71 | ]); 72 | 73 | expect(result).toEqual(['card-special', 'card-normal']); 74 | }); 75 | 76 | it('should not extract classes from binary comparison expressions', () => { 77 | const expression = "someValue === 'card' && otherValue !== 'btn'"; 78 | const astWithSource = parseNgClassExpression(expression); 79 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 80 | 'card', 81 | 'btn', 82 | ]); 83 | 84 | expect(result).toEqual([]); 85 | }); 86 | 87 | it('should handle complex nested expressions', () => { 88 | const expression = 89 | "[option?.logo?.toLowerCase() === 'card' ? 'card-active' : '', 'base-class']"; 90 | const astWithSource = parseNgClassExpression(expression); 91 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 92 | 'card', 93 | 'card-active', 94 | 'base-class', 95 | ]); 96 | 97 | expect(result).toEqual(['card-active', 'base-class']); 98 | }); 99 | 100 | it('should handle object with conditional values', () => { 101 | const expression = 102 | "{ 'card': option?.logo?.toLowerCase() === 'card', 'btn': isButton }"; 103 | const astWithSource = parseNgClassExpression(expression); 104 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 105 | 'card', 106 | 'btn', 107 | ]); 108 | 109 | expect(result).toEqual(['card', 'btn']); 110 | }); 111 | 112 | it('should handle spaced class names in object keys', () => { 113 | const expression = "{ 'card special': true, 'btn primary': false }"; 114 | const astWithSource = parseNgClassExpression(expression); 115 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 116 | 'card', 117 | 'special', 118 | 'btn', 119 | 'primary', 120 | ]); 121 | 122 | expect(result).toEqual(['card', 'special', 'btn', 'primary']); 123 | }); 124 | 125 | it('should return empty array for non-matching classes', () => { 126 | const expression = "['other', 'different']"; 127 | const astWithSource = parseNgClassExpression(expression); 128 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 129 | 'card', 130 | 'btn', 131 | ]); 132 | 133 | expect(result).toEqual([]); 134 | }); 135 | 136 | it('should remove duplicates', () => { 137 | const expression = "['card', 'card', 'btn']"; 138 | const astWithSource = parseNgClassExpression(expression); 139 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 140 | 'card', 141 | 'btn', 142 | ]); 143 | 144 | expect(result).toEqual(['card', 'btn']); 145 | }); 146 | }); 147 | 148 | describe('ngClassContainsClass', () => { 149 | it('should return true when class is found in AST', () => { 150 | const expression = "['btn', 'btn-primary']"; 151 | const astWithSource = parseNgClassExpression(expression); 152 | 153 | expect(ngClassContainsClass(astWithSource, 'btn')).toBe(true); 154 | expect(ngClassContainsClass(astWithSource, 'btn-primary')).toBe(true); 155 | }); 156 | 157 | it('should return false when class is not found in AST', () => { 158 | const expression = "['other', 'different']"; 159 | const astWithSource = parseNgClassExpression(expression); 160 | 161 | expect(ngClassContainsClass(astWithSource, 'btn')).toBe(false); 162 | }); 163 | 164 | it('should return false for classes in comparisons', () => { 165 | const expression = 166 | "option?.logo?.toLowerCase() === 'card' ? 'active' : 'inactive'"; 167 | const astWithSource = parseNgClassExpression(expression); 168 | 169 | // Should not find 'card' since it's in a comparison, not a class assignment 170 | expect(ngClassContainsClass(astWithSource, 'card')).toBe(false); 171 | expect(ngClassContainsClass(astWithSource, 'active')).toBe(true); 172 | }); 173 | }); 174 | 175 | describe('AST-based ngClass parsing integration tests', () => { 176 | it('should handle the original problem case correctly', () => { 177 | const expression = 178 | "[option?.logo?.toLowerCase(), option?.logo?.toLowerCase() == 'card' ? mergedCardLogoClass : '']"; 179 | const astWithSource = parseNgClassExpression(expression); 180 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, ['card']); 181 | 182 | // Should NOT find 'card' from the comparison 183 | expect(result).toEqual([]); 184 | }); 185 | 186 | it('should find card when actually used as a class', () => { 187 | const expression = "['card', 'other-class']"; 188 | const astWithSource = parseNgClassExpression(expression); 189 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, ['card']); 190 | 191 | expect(result).toEqual(['card']); 192 | }); 193 | 194 | it('should handle complex real-world scenarios', () => { 195 | const expression = 196 | "{ 'card': isCard, 'btn': isButton, 'active': option?.logo?.toLowerCase() === 'card' }"; 197 | const astWithSource = parseNgClassExpression(expression); 198 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 199 | 'card', 200 | 'btn', 201 | ]); 202 | 203 | expect(result).toEqual(['card', 'btn']); 204 | }); 205 | }); 206 | 207 | describe('AST-based class binding parsing', () => { 208 | function parseClassExpression(expression: string): ASTWithSource { 209 | const template = `<div [class]="${expression}"></div>`; 210 | const result = parseTemplate(template, 'test.html'); 211 | const element = result.nodes[0] as TmplAstElement; 212 | const classInput = element.inputs.find((input) => input.name === 'class'); 213 | return classInput?.value as ASTWithSource; 214 | } 215 | 216 | it('should work the same for [class] bindings with false positive avoidance', () => { 217 | const expression = 218 | "option?.logo?.toLowerCase() == 'card' ? 'card-special' : 'other'"; 219 | const astWithSource = parseClassExpression(expression); 220 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 221 | 'card', 222 | 'card-special', 223 | ]); 224 | 225 | expect(result).toEqual(['card-special']); 226 | }); 227 | 228 | it('should handle simple string literals in [class] bindings', () => { 229 | const expression = "'card btn-primary'"; 230 | const astWithSource = parseClassExpression(expression); 231 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 232 | 'card', 233 | 'btn-primary', 234 | 'other', 235 | ]); 236 | 237 | expect(result).toEqual(['card', 'btn-primary']); 238 | }); 239 | 240 | it('should handle ternary expressions in [class] bindings', () => { 241 | const expression = "isActive ? 'card active' : 'card inactive'"; 242 | const astWithSource = parseClassExpression(expression); 243 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 244 | 'card', 245 | 'active', 246 | 'inactive', 247 | ]); 248 | 249 | expect(result).toEqual(['card', 'active', 'inactive']); 250 | }); 251 | }); 252 | 253 | describe('AST-based interpolated class parsing', () => { 254 | function parseInterpolatedClassExpression(classValue: string): ASTWithSource { 255 | const template = `<div class="${classValue}"></div>`; 256 | const result = parseTemplate(template, 'test.html'); 257 | const element = result.nodes[0] as TmplAstElement; 258 | const classInput = element.inputs.find((input) => input.name === 'class'); 259 | return classInput?.value as ASTWithSource; 260 | } 261 | 262 | it('should extract classes from interpolated class attributes', () => { 263 | const classValue = 'count count-{{ size() }} other-class'; 264 | const astWithSource = parseInterpolatedClassExpression(classValue); 265 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 266 | 'count', 267 | 'other-class', 268 | 'different', 269 | ]); 270 | 271 | expect(result).toEqual(['count', 'other-class']); 272 | }); 273 | 274 | it('should handle interpolation with multiple dynamic parts', () => { 275 | const classValue = 'prefix {{ type }} middle {{ state }} suffix'; 276 | const astWithSource = parseInterpolatedClassExpression(classValue); 277 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 278 | 'prefix', 279 | 'middle', 280 | 'suffix', 281 | ]); 282 | 283 | expect(result).toEqual(['prefix', 'middle', 'suffix']); 284 | }); 285 | 286 | it('should handle interpolation with only static classes', () => { 287 | const classValue = 'btn btn-primary active'; 288 | const astWithSource = parseInterpolatedClassExpression(classValue); 289 | 290 | // Static class attributes without interpolation don't create class inputs 291 | // They are handled as static attributes, not dynamic bindings 292 | expect(astWithSource).toBeUndefined(); 293 | }); 294 | 295 | it('should avoid false positives from dynamic expressions in interpolation', () => { 296 | const classValue = "base-class {{ condition ? 'card' : 'other' }}"; 297 | const astWithSource = parseInterpolatedClassExpression(classValue); 298 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 299 | 'base-class', 300 | 'card', 301 | 'other', 302 | ]); 303 | 304 | expect(result).toEqual(['base-class']); 305 | }); 306 | 307 | it('should handle the exact failing test case', () => { 308 | const classValue = 'count count-{{ size() }} other-class'; 309 | const astWithSource = parseInterpolatedClassExpression(classValue); 310 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 311 | 'count', 312 | ]); 313 | 314 | expect(result).toEqual(['count']); 315 | }); 316 | }); 317 | 318 | describe('Real-world ngClass patterns from monorepo', () => { 319 | it('should handle complex object-style ngClass with multiple conditions', () => { 320 | // Based on promo filter popup component 321 | const expression = `{ 322 | 'disabled': selectedFilterIndexes?.totalCount === 0, 323 | 'pill-with-badge-v2': isOfferPageEnhanced, 324 | 'ml-2': true, 325 | 'promo-filter-apply': isOfferPageEnhanced 326 | }`; 327 | const astWithSource = parseNgClassExpression(expression); 328 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 329 | 'disabled', 330 | 'pill-with-badge-v2', 331 | 'ml-2', 332 | 'promo-filter-apply', 333 | 'btn-primary', 334 | ]); 335 | 336 | expect(result).toEqual([ 337 | 'disabled', 338 | 'pill-with-badge-v2', 339 | 'ml-2', 340 | 'promo-filter-apply', 341 | ]); 342 | }); 343 | 344 | it('should handle dark mode conditional classes', () => { 345 | const expression = `{ 346 | 'bg-body-10': !darkModeService.isEnabled, 347 | 'dark-mode-background': darkModeService.isEnabled 348 | }`; 349 | const astWithSource = parseNgClassExpression(expression); 350 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 351 | 'bg-body-10', 352 | 'dark-mode-background', 353 | 'other-class', 354 | ]); 355 | 356 | expect(result).toEqual(['bg-body-10', 'dark-mode-background']); 357 | }); 358 | 359 | it('should handle simple boolean-based ngClass', () => { 360 | const expression = `{ 'offers-navigation-enable-v1': subNavigationV1Enabled }`; 361 | const astWithSource = parseNgClassExpression(expression); 362 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 363 | 'offers-navigation-enable-v1', 364 | 'other-class', 365 | ]); 366 | 367 | expect(result).toEqual(['offers-navigation-enable-v1']); 368 | }); 369 | 370 | it('should handle responsive class conditions', () => { 371 | const expression = `{ 372 | 'mobile-card-middle': isMobile, 373 | 'btn-sm': isSmallScreen, 374 | 'form-width-75': !isMobile 375 | }`; 376 | const astWithSource = parseNgClassExpression(expression); 377 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 378 | 'mobile-card-middle', 379 | 'btn-sm', 380 | 'form-width-75', 381 | ]); 382 | 383 | expect(result).toEqual(['mobile-card-middle', 'btn-sm', 'form-width-75']); 384 | }); 385 | 386 | it('should handle property-based class binding', () => { 387 | const expression = `filterByProduct?.messages?.producticonclass`; 388 | const astWithSource = parseNgClassExpression(expression); 389 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 390 | 'icon-class', 391 | 'ui-icon', 392 | ]); 393 | 394 | expect(result).toEqual([]); 395 | }); 396 | 397 | it('should avoid false positives in complex comparison expressions', () => { 398 | const expression = `{ 399 | 'disabled': filtersByOffer?.count === 0, 400 | 'badge-counter': selectedFilterIndexes?.totalCount > 0, 401 | 'active': selectedFilters && selectedFilters.length > 0 402 | }`; 403 | const astWithSource = parseNgClassExpression(expression); 404 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 405 | 'disabled', 406 | 'badge-counter', 407 | 'active', 408 | 'count', 409 | 'selectedFilters', 410 | ]); 411 | 412 | expect(result).toEqual(['disabled', 'badge-counter', 'active']); 413 | }); 414 | 415 | it('should handle mixed static and conditional classes', () => { 416 | const expression = `[ 417 | 'navbar', 418 | 'navbar-expand-sm', 419 | condition ? 'sub-nav-wrapper' : '', 420 | menuContainer?.class 421 | ]`; 422 | const astWithSource = parseNgClassExpression(expression); 423 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 424 | 'navbar', 425 | 'navbar-expand-sm', 426 | 'sub-nav-wrapper', 427 | 'other-class', 428 | ]); 429 | 430 | expect(result).toEqual(['navbar', 'navbar-expand-sm', 'sub-nav-wrapper']); 431 | }); 432 | 433 | it('should handle the exact problematic pattern from the original issue', () => { 434 | // The exact pattern that was causing false positives 435 | const expression = `[ 436 | option?.logo?.toLowerCase(), 437 | option?.logo?.toLowerCase() == 'card' ? mergedCardLogoClass : '', 438 | 'base-class' 439 | ]`; 440 | const astWithSource = parseNgClassExpression(expression); 441 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 442 | 'card', 443 | 'base-class', 444 | 'other', 445 | ]); 446 | 447 | expect(result).toEqual(['base-class']); 448 | }); 449 | 450 | it('should handle nested object with spaced class names', () => { 451 | // Based on filter popup patterns 452 | const expression = `{ 453 | 'filter-product-icon ui-icon': true, 454 | 'ui-icon-size-lg': iconSize === 'large', 455 | 'status-pills transparent': hasTransparentStyle 456 | }`; 457 | const astWithSource = parseNgClassExpression(expression); 458 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 459 | 'filter-product-icon', 460 | 'ui-icon', 461 | 'ui-icon-size-lg', 462 | 'status-pills', 463 | 'transparent', 464 | 'large', 465 | ]); 466 | 467 | expect(result).toEqual([ 468 | 'filter-product-icon', 469 | 'ui-icon', 470 | 'ui-icon-size-lg', 471 | 'status-pills', 472 | 'transparent', 473 | ]); 474 | }); 475 | }); 476 | 477 | describe('Real-world [class] binding patterns', () => { 478 | // Helper function to extract AST from [class] expression 479 | function parseClassExpression(expression: string): ASTWithSource { 480 | const template = `<div [class]="${expression}"></div>`; 481 | const result = parseTemplate(template, 'test.html'); 482 | const element = result.nodes[0] as TmplAstElement; 483 | const classInput = element.inputs.find((input) => input.name === 'class'); 484 | return classInput?.value as ASTWithSource; 485 | } 486 | 487 | it('should handle conditional class strings', () => { 488 | const expression = `isActive ? 'nav-item active' : 'nav-item'`; 489 | const astWithSource = parseClassExpression(expression); 490 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 491 | 'nav-item', 492 | 'active', 493 | 'inactive', 494 | ]); 495 | 496 | expect(result).toEqual(['nav-item', 'active']); 497 | }); 498 | 499 | it('should handle complex ternary with comparison false positive potential', () => { 500 | const expression = `userType === 'admin' ? 'admin-panel active' : userType === 'user' ? 'user-panel' : 'guest-panel'`; 501 | const astWithSource = parseClassExpression(expression); 502 | const result = extractClassNamesFromNgClassAST(astWithSource.ast, [ 503 | 'admin', 504 | 'admin-panel', 505 | 'active', 506 | 'user', 507 | 'user-panel', 508 | 'guest-panel', 509 | ]); 510 | 511 | expect(result).toEqual([ 512 | 'admin-panel', 513 | 'active', 514 | 'user-panel', 515 | 'guest-panel', 516 | ]); 517 | }); 518 | }); 519 | 520 | describe('Backward compatibility - ngClassesIncludeClassName', () => { 521 | it('should work with object expressions', () => { 522 | const expression = `{ 'btn': true, 'btn-primary': isActive }`; 523 | expect(ngClassesIncludeClassName(expression, 'btn')).toBe(true); 524 | expect(ngClassesIncludeClassName(expression, 'btn-primary')).toBe(true); 525 | expect(ngClassesIncludeClassName(expression, 'btn-secondary')).toBe(false); 526 | }); 527 | 528 | it('should work with array expressions', () => { 529 | const expression = `['btn', 'btn-primary', condition ? 'active' : '']`; 530 | expect(ngClassesIncludeClassName(expression, 'btn')).toBe(true); 531 | expect(ngClassesIncludeClassName(expression, 'btn-primary')).toBe(true); 532 | expect(ngClassesIncludeClassName(expression, 'active')).toBe(true); 533 | expect(ngClassesIncludeClassName(expression, 'inactive')).toBe(false); 534 | }); 535 | 536 | it('should work with string expressions', () => { 537 | const expression = `'btn btn-primary active'`; 538 | expect(ngClassesIncludeClassName(expression, 'btn')).toBe(true); 539 | expect(ngClassesIncludeClassName(expression, 'btn-primary')).toBe(true); 540 | expect(ngClassesIncludeClassName(expression, 'active')).toBe(true); 541 | expect(ngClassesIncludeClassName(expression, 'inactive')).toBe(false); 542 | }); 543 | 544 | it('should handle basic string matching', () => { 545 | const expression = `{ 'btn': option?.logo?.toLowerCase() === 'card', 'active': true }`; 546 | expect(ngClassesIncludeClassName(expression, 'btn')).toBe(true); 547 | expect(ngClassesIncludeClassName(expression, 'active')).toBe(true); 548 | }); 549 | 550 | it('should handle missing classes', () => { 551 | const expression = `{ 'other': true }`; 552 | expect(ngClassesIncludeClassName(expression, 'btn')).toBe(false); 553 | }); 554 | }); 555 | ``` -------------------------------------------------------------------------------- /packages/minimal-repo/packages/design-system/storybook-host-app/src/components/badge/badge.component.stories.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { generateStatusBadges } from '@design-system/shared-storybook-utils'; 2 | import { DemoIconComponent } from '@design-system/storybook-demo-cmp-lib'; 3 | import { 4 | DS_BADGE_SIZE_ARRAY, 5 | DS_BADGE_VARIANT_ARRAY, 6 | DsBadge, 7 | } from '@frontend/ui/badge'; 8 | import { DsNotificationBubble } from '@frontend/ui/notification-bubble'; 9 | import { 10 | DsComplexityRatingComponent, 11 | getVariantInfo, 12 | } from '@frontend/ui/utils'; 13 | import { Meta, StoryObj, moduleMetadata } from '@storybook/angular'; 14 | 15 | import { allThemes } from '../../modes'; 16 | 17 | type DsBadgeStoryType = DsBadge & { label: string }; 18 | 19 | const meta: Meta<DsBadgeStoryType> = { 20 | title: 'Components/Badge', 21 | component: DsBadge, 22 | parameters: { 23 | status: generateStatusBadges('UX-2308', ['stable']), 24 | }, 25 | 26 | excludeStories: /.*Data$/, 27 | argTypes: { 28 | label: { 29 | type: 'string', 30 | table: { defaultValue: { summary: 'Label' } }, 31 | control: 'text', 32 | description: 'The text of the badge', 33 | }, 34 | size: { 35 | options: DS_BADGE_SIZE_ARRAY, 36 | table: { defaultValue: { summary: 'medium' }, category: 'Styling' }, 37 | control: { type: 'select' }, 38 | description: 'The size of the badge', 39 | }, 40 | variant: { 41 | options: DS_BADGE_VARIANT_ARRAY, 42 | table: { defaultValue: { summary: 'primary' }, category: 'Styling' }, 43 | control: { type: 'select' }, 44 | description: 'The variant of the badge', 45 | }, 46 | inverse: { 47 | type: 'boolean', 48 | table: { defaultValue: { summary: 'false' }, category: 'Styling' }, 49 | control: { type: 'boolean' }, 50 | description: 'The inverse state of the badge', 51 | }, 52 | disabled: { 53 | type: 'boolean', 54 | table: { 55 | defaultValue: { summary: 'false' }, 56 | }, 57 | control: { type: 'boolean' }, 58 | description: 'The disabled state of the badges', 59 | }, 60 | }, 61 | args: { 62 | label: 'Label', 63 | size: 'medium', 64 | variant: 'primary', 65 | disabled: false, 66 | inverse: false, 67 | }, 68 | decorators: [ 69 | moduleMetadata({ imports: [DemoIconComponent, DsNotificationBubble] }), 70 | ], 71 | }; 72 | 73 | export default meta; 74 | 75 | type Story = StoryObj<DsBadgeStoryType>; 76 | 77 | export const Default: Story = { 78 | parameters: { 79 | name: 'Default', 80 | design: { 81 | name: 'Whitelabel', 82 | type: 'figma', 83 | url: 'https://www.figma.com/file/NgrOt8MGJhe0obKFBQgqdT/Component-Tokens-(POC)?type=design&node-id=16640-68740&&mode=design&t=fS1qO73SS8lGciLj-4', 84 | }, 85 | }, 86 | name: 'Default', 87 | args: { 88 | ...meta.args, 89 | }, 90 | render: (badge) => ({ 91 | template: ` 92 | <ds-badge variant="${badge.variant}" size="${badge.size}" disabled="${badge.disabled}" [inverse]="${badge.inverse}"> 93 | ${badge.label} 94 | </ds-badge> 95 | `, 96 | }), 97 | }; 98 | 99 | export const WithIcon: Story = { 100 | parameters: { 101 | name: 'Default', 102 | design: { 103 | name: 'Whitelabel', 104 | type: 'figma', 105 | url: 'https://www.figma.com/file/NgrOt8MGJhe0obKFBQgqdT/Component-Tokens-(POC)?type=design&node-id=16640-68740&&mode=design&t=fS1qO73SS8lGciLj-4', 106 | }, 107 | }, 108 | args: { 109 | ...Default.args, 110 | }, 111 | argTypes: { 112 | ...Default.argTypes, 113 | size: { 114 | options: ['medium', 'xsmall'], 115 | }, 116 | }, 117 | decorators: [moduleMetadata({ imports: [DsBadge] })], 118 | render: (badge) => ({ 119 | template: ` 120 | <ds-badge variant="${badge.variant}" size="${badge.size}" disabled="${badge.disabled}" [inverse]="${badge.inverse}"> 121 | ${badge.label} 122 | <ds-demo-icon slot="start"/> 123 | </ds-badge> 124 | `, 125 | }), 126 | }; 127 | 128 | export const WithIconOnly: Story = { 129 | parameters: { 130 | name: 'Default', 131 | design: { 132 | name: 'Whitelabel', 133 | type: 'figma', 134 | url: 'https://www.figma.com/file/NgrOt8MGJhe0obKFBQgqdT/Component-Tokens-(POC)?type=design&node-id=16640-68740&&mode=design&t=fS1qO73SS8lGciLj-4', 135 | }, 136 | }, 137 | args: { 138 | ...Default.args, 139 | label: '', 140 | }, 141 | argTypes: { 142 | ...Default.argTypes, 143 | size: { 144 | options: ['medium', 'xsmall'], 145 | }, 146 | }, 147 | decorators: [moduleMetadata({ imports: [DsBadge] })], 148 | render: (badge) => ({ 149 | template: ` 150 | <ds-badge variant="${badge.variant}" size="${badge.size}" disabled="${badge.disabled}" [inverse]="${badge.inverse}"> 151 | ${badge.label} 152 | <ds-demo-icon slot="start"/> 153 | </ds-badge> 154 | `, 155 | }), 156 | }; 157 | 158 | export const WithNotificationBubble: Story = { 159 | parameters: { 160 | name: 'Default', 161 | design: { 162 | name: 'Whitelabel', 163 | type: 'figma', 164 | url: 'https://www.figma.com/file/NgrOt8MGJhe0obKFBQgqdT/Component-Tokens-(POC)?type=design&node-id=16640-68740&&mode=design&t=fS1qO73SS8lGciLj-4', 165 | }, 166 | }, 167 | args: { 168 | ...Default.args, 169 | }, 170 | argTypes: { 171 | ...Default.argTypes, 172 | size: { 173 | options: ['medium'], 174 | }, 175 | }, 176 | decorators: [moduleMetadata({ imports: [DsBadge] })], 177 | render: (badge) => ({ 178 | template: ` 179 | <ds-badge variant="${badge.variant}" size="${badge.size}" disabled="${badge.disabled}" [inverse]="${badge.inverse}"> 180 | ${badge.label} 181 | <ds-notification-bubble slot="start" variant="neutral" disabled="false" size="small" inverse="false">0</ds-notification-bubble> 182 | </ds-badge> 183 | `, 184 | }), 185 | }; 186 | 187 | export const variantInfo = { 188 | ...getVariantInfo(meta), 189 | tags: ['docs-template'], 190 | }; 191 | 192 | export const ComplexityLevel: Story = { 193 | tags: ['docs-template'], 194 | 195 | render: () => ({ 196 | moduleMetadata: { 197 | imports: [DsComplexityRatingComponent], 198 | }, 199 | template: ` 200 | <ds-complexity-rating 201 | [totalVariants]="variantInfo.totalCombinations" 202 | [variantOptions]="variantInfo.variantOptions" 203 | [sizeOptions]="variantInfo.sizeOptions" 204 | ></ds-complexity-rating> 205 | `, 206 | props: { 207 | variantInfo, 208 | }, 209 | }), 210 | }; 211 | 212 | export const WithSuccess: Story = { 213 | parameters: { 214 | name: 'Default', 215 | design: { 216 | name: 'Whitelabel', 217 | type: 'figma', 218 | url: 'https://www.figma.com/file/NgrOt8MGJhe0obKFBQgqdT/Component-Tokens-(POC)?type=design&node-id=16640-68740&&mode=design&t=fS1qO73SS8lGciLj-4', 219 | }, 220 | }, 221 | args: { 222 | ...Default.args, 223 | }, 224 | 225 | decorators: [moduleMetadata({})], 226 | render: (badge) => ({ 227 | template: ` 228 | <ds-badge variant="${badge.variant}" size="${badge.size}" disabled="${badge.disabled}" [inverse]="${badge.inverse}"> 229 | ${badge.label} 230 | <ds-demo-icon iconName="success" slot="end" /> 231 | </ds-badge> 232 | `, 233 | }), 234 | }; 235 | 236 | export const WithIconAndSuccess: Story = { 237 | parameters: { 238 | name: 'Default', 239 | design: { 240 | name: 'Whitelabel', 241 | type: 'figma', 242 | url: 'https://www.figma.com/file/NgrOt8MGJhe0obKFBQgqdT/Component-Tokens-(POC)?type=design&node-id=16640-68740&&mode=design&t=fS1qO73SS8lGciLj-4', 243 | }, 244 | }, 245 | args: { 246 | ...WithIcon.args, 247 | }, 248 | 249 | decorators: [moduleMetadata({})], 250 | render: (badge) => ({ 251 | template: ` 252 | <ds-badge variant="${badge.variant}" size="${badge.size}" disabled="${badge.disabled}" [inverse]="${badge.inverse}"> 253 | ${badge.label} 254 | <ds-demo-icon slot="start"/> 255 | <ds-demo-icon iconName="success" slot="end" /> 256 | </ds-badge> 257 | `, 258 | }), 259 | }; 260 | 261 | export const LargeExamples: Story = { 262 | tags: ['docs-template'], 263 | render: (args) => ({ 264 | props: args, 265 | template: ` 266 | <div style="display: grid; grid-template-columns: 1fr repeat(3, 1fr); grid-row-gap: 1em; grid-column-gap: 1em; text-align: left; align-items: center;"> 267 | ${renderHeaderRow( 268 | 'Primary', 269 | 'Primary', 270 | 'Primary Strong', 271 | 'Primary Subtle', 272 | )} 273 | ${renderPrimaryRows('medium')} 274 | <div style="grid-column: 1 / -1;"> 275 | <hr style="width: 100%; border: 1px solid #ddd; margin: 20px 0;"> 276 | </div> 277 | ${renderHeaderRow( 278 | 'Secondary', 279 | 'Secondary', 280 | 'Secondary Strong', 281 | 'Secondary Subtle', 282 | )} 283 | ${renderSecondaryRows('medium')} 284 | <div style="grid-column: 1 / -1;"> 285 | <hr style="width: 100%; border: 1px solid #ddd; margin: 20px 0;"> 286 | </div> 287 | ${renderHeaderRow( 288 | 'Green', 289 | 'Green', 290 | 'Green Strong', 291 | 'Green Subtle', 292 | )} 293 | ${renderGreenRows('medium')} 294 | <div style="grid-column: 1 / -1;"> 295 | <hr style="width: 100%; border: 1px solid #ddd; margin: 20px 0;"> 296 | </div> 297 | ${renderHeaderRow('Blue', 'Blue', 'Blue Strong', 'Blue Subtle')} 298 | ${renderBlueRows('medium')} 299 | <div style="grid-column: 1 / -1;"> 300 | <hr style="width: 100%; border: 1px solid #ddd; margin: 20px 0;"> 301 | </div> 302 | ${renderHeaderRow('Red', 'Red', 'Red Strong', 'Red Subtle')} 303 | ${renderRedRows('medium')} 304 | <div style="grid-column: 1 / -1;"> 305 | <hr style="width: 100%; border: 1px solid #ddd; margin: 20px 0;"> 306 | </div> 307 | ${renderHeaderRow( 308 | 'Neutral', 309 | 'Neutral', 310 | 'Neutral Strong', 311 | 'Neutral Subtle', 312 | )} 313 | ${renderNeutralRows('medium')} 314 | <div style="grid-column: 1 / -1;"> 315 | <hr style="width: 100%; border: 1px solid #ddd; margin: 20px 0;"> 316 | </div> 317 | ${renderHeaderRow( 318 | 'Purple', 319 | 'Purple', 320 | 'Purple Strong', 321 | 'Purple Subtle', 322 | )} 323 | ${renderPurpleRows('medium')} 324 | <div style="grid-column: 1 / -1;"> 325 | <hr style="width: 100%; border: 1px solid #ddd; margin: 20px 0;"> 326 | </div> 327 | ${renderHeaderRow( 328 | 'Yellow', 329 | 'Yellow', 330 | 'Yellow Strong', 331 | 'Yellow Subtle', 332 | )} 333 | ${renderYellowRows('medium')} 334 | <div style="grid-column: 1 / -1;"> 335 | <hr style="width: 100%; border: 1px solid #ddd; margin: 20px 0;"> 336 | </div> 337 | ${renderHeaderRow( 338 | 'Orange', 339 | 'Orange', 340 | 'Orange Strong', 341 | 'Orange Subtle', 342 | )} 343 | ${renderOrangeRows('medium')} 344 | <div style="grid-column: 1 / -1;"> 345 | <hr style="width: 100%; border: 1px solid #ddd; margin: 20px 0;"> 346 | </div> 347 | </div> 348 | `, 349 | }), 350 | }; 351 | 352 | function renderHeaderRow(title = '', col1 = '', col2 = '', col3 = '') { 353 | return ` 354 | <div style="grid-column: 1 / span 1; text-align: left; margin-top: 20px;"> 355 | <strong>${title}</strong> 356 | </div> 357 | <div style="grid-column: 2 / span 1; text-align: left; margin-top: 20px;"> 358 | <strong>${col1}</strong> 359 | </div> 360 | <div style="grid-column: 3 / span 1; text-align: left; margin-top: 20px;"> 361 | <strong>${col2}</strong> 362 | </div> 363 | <div style="grid-column: 4 / span 1; text-align: left; margin-top: 20px;"> 364 | <strong>${col3}</strong> 365 | </div> 366 | `; 367 | } 368 | 369 | function renderPrimaryRows(size: string) { 370 | return ` 371 | ${renderRow('No Icon, No Success', size)} 372 | ${renderRow('Icon', size, { showIcon: true })} 373 | ${renderRow('Icon Only', size, { showIcon: true, showIconOnly: true })} 374 | ${renderRow('Notification Bubble', size, { 375 | showNotificationBubble: true, 376 | })} 377 | ${renderRow('Success', size, { showSuccess: true })} 378 | ${renderRow('Icon + Success', size, { 379 | showIcon: true, 380 | showSuccess: true, 381 | })} 382 | `; 383 | } 384 | 385 | function renderSecondaryRows(size: string) { 386 | return ` 387 | ${renderRow('No Icon, No Success', size, { 388 | variantPrefix: 'secondary', 389 | })} 390 | ${renderRow('Icon', size, { 391 | showIcon: true, 392 | variantPrefix: 'secondary', 393 | })} 394 | ${renderRow('Icon Only', size, { 395 | showIcon: true, 396 | showIconOnly: true, 397 | variantPrefix: 'secondary', 398 | })} 399 | ${renderRow('Notification Bubble', size, { 400 | showNotificationBubble: true, 401 | variantPrefix: 'secondary', 402 | })} 403 | ${renderRow('Success', size, { 404 | showSuccess: true, 405 | variantPrefix: 'secondary', 406 | })} 407 | ${renderRow('Icon + Success', size, { 408 | showIcon: true, 409 | showSuccess: true, 410 | variantPrefix: 'secondary', 411 | })} 412 | `; 413 | } 414 | 415 | function renderGreenRows(size: string) { 416 | return ` 417 | ${renderRow('No Icon, No Success', size, { variantPrefix: 'green' })} 418 | ${renderRow('Icon', size, { showIcon: true, variantPrefix: 'green' })} 419 | ${renderRow('Icon Only', size, { 420 | showIcon: true, 421 | showIconOnly: true, 422 | variantPrefix: 'green', 423 | })} 424 | ${renderRow('Notification Bubble', size, { 425 | showNotificationBubble: true, 426 | variantPrefix: 'green', 427 | })} 428 | ${renderRow('Success', size, { 429 | showSuccess: true, 430 | variantPrefix: 'green', 431 | })} 432 | ${renderRow('Icon + Success', size, { 433 | showIcon: true, 434 | showSuccess: true, 435 | variantPrefix: 'green', 436 | })} 437 | `; 438 | } 439 | 440 | function renderBlueRows(size: string) { 441 | return ` 442 | ${renderRow('No Icon, No Success', size, { variantPrefix: 'blue' })} 443 | ${renderRow('Icon', size, { showIcon: true, variantPrefix: 'blue' })} 444 | ${renderRow('Icon Only', size, { 445 | showIcon: true, 446 | showIconOnly: true, 447 | variantPrefix: 'blue', 448 | })} 449 | ${renderRow('Notification Bubble', size, { 450 | showNotificationBubble: true, 451 | variantPrefix: 'blue', 452 | })} 453 | ${renderRow('Success', size, { 454 | showSuccess: true, 455 | variantPrefix: 'blue', 456 | })} 457 | ${renderRow('Icon + Success', size, { 458 | showIcon: true, 459 | showSuccess: true, 460 | variantPrefix: 'blue', 461 | })} 462 | `; 463 | } 464 | 465 | function renderRedRows(size: string) { 466 | return ` 467 | ${renderRow('No Icon, No Success', size, { variantPrefix: 'red' })} 468 | ${renderRow('Icon', size, { showIcon: true, variantPrefix: 'red' })} 469 | ${renderRow('Icon Only', size, { 470 | showIcon: true, 471 | showIconOnly: true, 472 | variantPrefix: 'red', 473 | })} 474 | ${renderRow('Notification Bubble', size, { 475 | showNotificationBubble: true, 476 | variantPrefix: 'red', 477 | })} 478 | ${renderRow('Success', size, { 479 | showSuccess: true, 480 | variantPrefix: 'red', 481 | })} 482 | ${renderRow('Icon + Success', size, { 483 | showIcon: true, 484 | showSuccess: true, 485 | variantPrefix: 'red', 486 | })} 487 | `; 488 | } 489 | 490 | function renderNeutralRows(size: string) { 491 | return ` 492 | ${renderRow('No Icon, No Success', size, { variantPrefix: 'neutral' })} 493 | ${renderRow('Icon', size, { showIcon: true, variantPrefix: 'neutral' })} 494 | ${renderRow('Icon Only', size, { 495 | showIcon: true, 496 | showIconOnly: true, 497 | variantPrefix: 'neutral', 498 | })} 499 | ${renderRow('Notification Bubble', size, { 500 | showNotificationBubble: true, 501 | variantPrefix: 'neutral', 502 | })} 503 | ${renderRow('Success', size, { 504 | showSuccess: true, 505 | variantPrefix: 'neutral', 506 | })} 507 | ${renderRow('Icon + Success', size, { 508 | showIcon: true, 509 | showSuccess: true, 510 | variantPrefix: 'neutral', 511 | })} 512 | `; 513 | } 514 | 515 | function renderPurpleRows(size: string) { 516 | return ` 517 | ${renderRow('No Icon, No Success', size, { variantPrefix: 'purple' })} 518 | ${renderRow('Icon', size, { showIcon: true, variantPrefix: 'purple' })} 519 | ${renderRow('Icon Only', size, { 520 | showIcon: true, 521 | showIconOnly: true, 522 | variantPrefix: 'purple', 523 | })} 524 | ${renderRow('Notification Bubble', size, { 525 | showNotificationBubble: true, 526 | variantPrefix: 'purple', 527 | })} 528 | ${renderRow('Success', size, { 529 | showSuccess: true, 530 | variantPrefix: 'purple', 531 | })} 532 | ${renderRow('Icon + Success', size, { 533 | showIcon: true, 534 | showSuccess: true, 535 | variantPrefix: 'purple', 536 | })} 537 | `; 538 | } 539 | 540 | function renderYellowRows(size: string) { 541 | return ` 542 | ${renderRow('No Icon, No Success', size, { variantPrefix: 'yellow' })} 543 | ${renderRow('Icon', size, { showIcon: true, variantPrefix: 'yellow' })} 544 | ${renderRow('Icon Only', size, { 545 | showIcon: true, 546 | showIconOnly: true, 547 | variantPrefix: 'yellow', 548 | })} 549 | ${renderRow('Notification Bubble', size, { 550 | showNotificationBubble: true, 551 | variantPrefix: 'yellow', 552 | })} 553 | ${renderRow('Success', size, { 554 | showSuccess: true, 555 | variantPrefix: 'yellow', 556 | })} 557 | ${renderRow('Icon + Success', size, { 558 | showIcon: true, 559 | showSuccess: true, 560 | variantPrefix: 'yellow', 561 | })} 562 | `; 563 | } 564 | 565 | function renderOrangeRows(size: string) { 566 | return ` 567 | ${renderRow('No Icon, No Success', size, { variantPrefix: 'orange' })} 568 | ${renderRow('Icon', size, { showIcon: true, variantPrefix: 'orange' })} 569 | ${renderRow('Icon Only', size, { 570 | showIcon: true, 571 | showIconOnly: true, 572 | variantPrefix: 'orange', 573 | })} 574 | ${renderRow('Notification Bubble', size, { 575 | showNotificationBubble: true, 576 | variantPrefix: 'orange', 577 | })} 578 | ${renderRow('Success', size, { 579 | showSuccess: true, 580 | variantPrefix: 'orange', 581 | })} 582 | ${renderRow('Icon + Success', size, { 583 | showIcon: true, 584 | showSuccess: true, 585 | variantPrefix: 'orange', 586 | })} 587 | `; 588 | } 589 | 590 | function renderRow( 591 | name: string, 592 | size: string, 593 | { 594 | showSuccess = false, 595 | showIcon = false, 596 | showIconOnly = false, 597 | showNotificationBubble = false, 598 | variantPrefix = 'primary', 599 | } = {}, 600 | ) { 601 | return ` 602 | <span style="grid-column: 1 / span 1; align-self: center;">${name}</span> 603 | ${renderBadge( 604 | `${variantPrefix}`, 605 | size, 606 | showIcon, 607 | showIconOnly, 608 | showNotificationBubble, 609 | showSuccess, 610 | )} 611 | ${renderBadge( 612 | `${variantPrefix}-strong`, 613 | size, 614 | showIcon, 615 | showIconOnly, 616 | showNotificationBubble, 617 | showSuccess, 618 | )} 619 | ${renderBadge( 620 | `${variantPrefix}-subtle`, 621 | size, 622 | showIcon, 623 | showIconOnly, 624 | showNotificationBubble, 625 | showSuccess, 626 | )} 627 | `; 628 | } 629 | 630 | function renderBadge( 631 | variant: string, 632 | size: string, 633 | showIcon: boolean, 634 | showIconOnly: boolean, 635 | showNotificationBubble: boolean, 636 | showSuccess: boolean, 637 | ) { 638 | return ` 639 | <div style="grid-column: span 1;"> 640 | <ds-badge variant="${variant}" size="${size}"> 641 | ${showIconOnly ? '' : 'Label'} 642 | ${showIcon ? '<ds-demo-icon slot="start"/>' : ''} 643 | ${ 644 | showNotificationBubble 645 | ? '<ds-notification-bubble slot="start" variant="neutral" disabled="false" size="small" inverse="false">0</ds-notification-bubble>' 646 | : '' 647 | } 648 | ${ 649 | showSuccess 650 | ? '<ds-demo-icon iconName="success" slot="end"/>' 651 | : '' 652 | } 653 | </ds-badge> 654 | </div> 655 | `; 656 | } 657 | export const ChromaticTestStory: Story = { 658 | tags: ['docs-template'], 659 | parameters: { 660 | chromatic: { 661 | modes: allThemes, 662 | disableSnapshot: false, 663 | }, 664 | }, 665 | render: (args) => ({ 666 | props: args, 667 | template: ` 668 | <div style="display: grid; grid-template-columns: repeat(7, 1fr); grid-row-gap: 1em; grid-column-gap: 1em; align-items: center; justify-items: center;"> 669 | 670 | 671 | ${renderHeaderRow()} 672 | 673 | ${renderRow('Xsmall', 'xsmall')} 674 | 675 | <div style="grid-column: span 7"></div> 676 | ${renderRow('Medium', 'medium')} 677 | ${renderRow('Medium, Icon', 'medium', { showIcon: true })} 678 | ${renderRow('Medium, IconOnly', 'medium', { 679 | showIcon: true, 680 | showIconOnly: true, 681 | })} 682 | ${renderRow('Medium, NotificationBubble', 'medium', { 683 | showNotificationBubble: true, 684 | })} 685 | ${renderRow('Medium, Success', 'medium', { showSuccess: true })} 686 | ${renderRow('Medium, Icon + Success', 'medium', { 687 | showIcon: true, 688 | showSuccess: true, 689 | })} 690 | 691 | <div style="grid-column: span 7"></div> 692 | ${renderRow('Large', 'medium')} 693 | ${renderRow('Large, Icon', 'medium', { showIcon: true })} 694 | ${renderRow('Large, IconOnly', 'medium', { 695 | showIcon: true, 696 | showIconOnly: true, 697 | })} 698 | ${renderRow('Large, NotificationBubble', 'medium', { 699 | showNotificationBubble: true, 700 | })} 701 | ${renderRow('Large, Success', 'medium', { showSuccess: true })} 702 | ${renderRow('Large, Icon + Success', 'medium', { 703 | showIcon: true, 704 | showSuccess: true, 705 | })} 706 | </div> 707 | `, 708 | }), 709 | }; 710 | ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/docs/examples/report.md: -------------------------------------------------------------------------------- ```markdown 1 | # Code PushUp Report 2 | 3 | | 🏷 Category | ⭐ Score | 🛡 Audits | 4 | | :------------------------------------------------ | :-------: | :-------: | 5 | | [Design System Coverage](#design-system-coverage) | 🔴 **38** | 13 | 6 | 7 | ## 🏷 Categories 8 | 9 | ### Design System Coverage 10 | 11 | Usage of design system components 12 | 13 | 🔴 Score: **38** 14 | 15 | - 🟥 [Usage coverage for DSButton component](#usage-coverage-for-dsbutton-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **7 classes found** 16 | - 🟥 [Usage coverage for DSAlert component](#usage-coverage-for-dsalert-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **6 classes found** 17 | - 🟥 [Usage coverage for DSTooltip component](#usage-coverage-for-dstooltip-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **5 classes found** 18 | - 🟥 [Usage coverage for DSBreadcrumb component](#usage-coverage-for-dsbreadcrumb-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **4 classes found** 19 | - 🟥 [Usage coverage for DSDropdown component](#usage-coverage-for-dsdropdown-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **4 classes found** 20 | - 🟥 [Usage coverage for DSModal component](#usage-coverage-for-dsmodal-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **4 classes found** 21 | - 🟥 [Usage coverage for DSProgressBar component](#usage-coverage-for-dsprogressbar-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **4 classes found** 22 | - 🟥 [Usage coverage for DSInput component](#usage-coverage-for-dsinput-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **1 class found** 23 | - 🟩 [Usage coverage for DSAccordion component](#usage-coverage-for-dsaccordion-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **0 classes found** 24 | - 🟩 [Usage coverage for DSCard component](#usage-coverage-for-dscard-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **0 classes found** 25 | - 🟩 [Usage coverage for DSNavbar component](#usage-coverage-for-dsnavbar-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **0 classes found** 26 | - 🟩 [Usage coverage for DSSlider component](#usage-coverage-for-dsslider-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **0 classes found** 27 | - 🟩 [Usage coverage for DSTabsModule component](#usage-coverage-for-dstabsmodule-component-angular-design-system-coverage) (_Angular Design System Coverage_) - **0 classes found** 28 | 29 | ## 🛡️ Audits 30 | 31 | ### Usage coverage for DSButton component (Angular Design System Coverage) 32 | 33 | <details> 34 | <summary>🟥 <b>7 classes found</b> (score: 0)</summary> 35 | 36 | #### Issues 37 | 38 | | Severity | Message | Source file | Line(s) | 39 | | :--------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: | 40 | | 🚨 _error_ | ✏️🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn</code>. Use <code>DSButton</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-button--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-button-dropdown.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-button-dropdown.component.ts) | 6 | 41 | | 🚨 _error_ | ✏️🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn-primary</code>. Use <code>DSButton</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-button--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-button-dropdown.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-button-dropdown.component.ts) | 6 | 42 | | 🚨 _error_ | ✏️🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn</code>. Use <code>DSButton</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-button--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts) | 10 | 43 | | 🚨 _error_ | ✏️🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn-primary</code>. Use <code>DSButton</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-button--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts) | 10 | 44 | | 🚨 _error_ | 🔗🎨 ️ The selector's class <code>btn</code> is deprecated. Use <code>DSButton</code> and delete the styles. <a href="https://storybook.company.com/latest/?path=/docs/components-button--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css) | 7-14 | 45 | | 🚨 _error_ | 🔗🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn</code>. Use <code>DSButton</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-button--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html) | 6 | 46 | | 🚨 _error_ | 🔗🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn-primary</code>. Use <code>DSButton</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-button--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html) | 6 | 47 | 48 | </details> 49 | 50 | Coverage audit for DSButton component. Matching classes: btn, btn-primary, legacy-button 51 | 52 | ### Usage coverage for DSAlert component (Angular Design System Coverage) 53 | 54 | <details> 55 | <summary>🟥 <b>6 classes found</b> (score: 0)</summary> 56 | 57 | #### Issues 58 | 59 | | Severity | Message | Source file | Line(s) | 60 | | :--------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: | 61 | | 🚨 _error_ | ✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>alert</code>. Use <code>DSAlert</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-alert--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert-tooltip-input.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert-tooltip-input.component.ts) | 6 | 62 | | 🚨 _error_ | ✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>alert</code>. Use <code>DSAlert</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-alert--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert.component.ts) | 5 | 63 | | 🚨 _error_ | ✏️🎨 ️ The selector's class <code>alert</code> is deprecated. Use <code>DSAlert</code> and delete the styles. <a href="https://storybook.company.com/latest/?path=/docs/components-alert--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert.component.ts) | 7-10 | 64 | | 🚨 _error_ | ✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>alert</code>. Use <code>DSAlert</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-alert--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts) | 46 | 65 | | 🚨 _error_ | 🔗🎨 ️ The selector's class <code>alert</code> is deprecated. Use <code>DSAlert</code> and delete the styles. <a href="https://storybook.company.com/latest/?path=/docs/components-alert--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css) | 43-47 | 66 | | 🚨 _error_ | 🔗🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>alert</code>. Use <code>DSAlert</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-alert--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html) | 22 | 67 | 68 | </details> 69 | 70 | Coverage audit for DSAlert component. Matching classes: alert, notification, legacy-alert 71 | 72 | ### Usage coverage for DSTooltip component (Angular Design System Coverage) 73 | 74 | <details> 75 | <summary>🟥 <b>5 classes found</b> (score: 0)</summary> 76 | 77 | #### Issues 78 | 79 | | Severity | Message | Source file | Line(s) | 80 | | :--------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: | 81 | | 🚨 _error_ | ✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>tooltip</code>. Use <code>DSTooltip</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-tooltip--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert-tooltip-input.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert-tooltip-input.component.ts) | 8 | 82 | | 🚨 _error_ | ✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>tooltip</code>. Use <code>DSTooltip</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-tooltip--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts) | 52 | 83 | | 🚨 _error_ | 🔗🎨 ️ The selector's class <code>tooltip</code> is deprecated. Use <code>DSTooltip</code> and delete the styles. <a href="https://storybook.company.com/latest/?path=/docs/components-tooltip--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css) | 57-61 | 84 | | 🚨 _error_ | 🔗🎨 ️ The selector's class <code>tooltip</code> is deprecated. Use <code>DSTooltip</code> and delete the styles. <a href="https://storybook.company.com/latest/?path=/docs/components-tooltip--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css) | 63-73 | 85 | | 🚨 _error_ | 🔗🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>tooltip</code>. Use <code>DSTooltip</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-tooltip--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html) | 31 | 86 | 87 | </details> 88 | 89 | Coverage audit for DSTooltip component. Matching classes: tooltip, legacy-tooltip, info-bubble 90 | 91 | ### Usage coverage for DSBreadcrumb component (Angular Design System Coverage) 92 | 93 | <details> 94 | <summary>🟥 <b>4 classes found</b> (score: 0)</summary> 95 | 96 | #### Issues 97 | 98 | | Severity | Message | Source file | Line(s) | 99 | | :--------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: | 100 | | 🚨 _error_ | ✏️🔲 Element <code>nav</code> in attribute <code>class</code> uses deprecated class <code>breadcrumb</code>. Use <code>DSBreadcrumb</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-breadcrumb--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts) | 62-64 | 101 | | 🚨 _error_ | 🔗🎨 ️ The selector's class <code>breadcrumb</code> is deprecated. Use <code>DSBreadcrumb</code> and delete the styles. <a href="https://storybook.company.com/latest/?path=/docs/components-breadcrumb--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css) | 76-79 | 102 | | 🚨 _error_ | 🔗🎨 ️ The selector's class <code>breadcrumb</code> is deprecated. Use <code>DSBreadcrumb</code> and delete the styles. <a href="https://storybook.company.com/latest/?path=/docs/components-breadcrumb--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css) | 81-84 | 103 | | 🚨 _error_ | 🔗🔲 Element <code>nav</code> in attribute <code>class</code> uses deprecated class <code>breadcrumb</code>. Use <code>DSBreadcrumb</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-breadcrumb--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html) | 34-36 | 104 | 105 | </details> 106 | 107 | Coverage audit for DSBreadcrumb component. Matching classes: breadcrumb, legacy-breadcrumb, nav-breadcrumb 108 | 109 | ### Usage coverage for DSDropdown component (Angular Design System Coverage) 110 | 111 | <details> 112 | <summary>🟥 <b>4 classes found</b> (score: 0)</summary> 113 | 114 | #### Issues 115 | 116 | | Severity | Message | Source file | Line(s) | 117 | | :--------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: | 118 | | 🚨 _error_ | ✏️🔲 Element <code>select</code> in attribute <code>class</code> uses deprecated class <code>dropdown</code>. Use <code>DSDropdown</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-dropdown--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-button-dropdown.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-button-dropdown.component.ts) | 7-10 | 119 | | 🚨 _error_ | ✏️🔲 Element <code>select</code> in attribute <code>class</code> uses deprecated class <code>dropdown</code>. Use <code>DSDropdown</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-dropdown--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts) | 37-40 | 120 | | 🚨 _error_ | 🔗🎨 ️ The selector's class <code>dropdown</code> is deprecated. Use <code>DSDropdown</code> and delete the styles. <a href="https://storybook.company.com/latest/?path=/docs/components-dropdown--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css) | 50-54 | 121 | | 🚨 _error_ | 🔗🔲 Element <code>select</code> in attribute <code>class</code> uses deprecated class <code>dropdown</code>. Use <code>DSDropdown</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-dropdown--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html) | 25-28 | 122 | 123 | </details> 124 | 125 | Coverage audit for DSDropdown component. Matching classes: dropdown, legacy-dropdown, custom-dropdown 126 | 127 | ### Usage coverage for DSModal component (Angular Design System Coverage) 128 | 129 | <details> 130 | <summary>🟥 <b>4 classes found</b> (score: 0)</summary> 131 | 132 | #### Issues 133 | 134 | | Severity | Message | Source file | Line(s) | 135 | | :--------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: | 136 | | 🚨 _error_ | ✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>modal</code>. Use <code>DSModal</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-modal--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts) | 18-23 | 137 | | 🚨 _error_ | ✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>modal</code>. Use <code>DSModal</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-modal--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-modal-progress.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-modal-progress.component.ts) | 6-11 | 138 | | 🚨 _error_ | 🔗🎨 ️ The selector's class <code>modal</code> is deprecated. Use <code>DSModal</code> and delete the styles. <a href="https://storybook.company.com/latest/?path=/docs/components-modal--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css) | 17-25 | 139 | | 🚨 _error_ | 🔗🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>modal</code>. Use <code>DSModal</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-modal--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html) | 9-14 | 140 | 141 | </details> 142 | 143 | Coverage audit for DSModal component. Matching classes: modal, popup, legacy-dialog 144 | 145 | ### Usage coverage for DSProgressBar component (Angular Design System Coverage) 146 | 147 | <details> 148 | <summary>🟥 <b>4 classes found</b> (score: 0)</summary> 149 | 150 | #### Issues 151 | 152 | | Severity | Message | Source file | Line(s) | 153 | | :--------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: | 154 | | 🚨 _error_ | ✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>progress-bar</code>. Use <code>DSProgressBar</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-progressbar--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts) | 29-31 | 155 | | 🚨 _error_ | ✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>progress-bar</code>. Use <code>DSProgressBar</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-progressbar--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-modal-progress.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-modal-progress.component.ts) | 13-15 | 156 | | 🚨 _error_ | 🔗🎨 ️ The selector's class <code>progress-bar</code> is deprecated. Use <code>DSProgressBar</code> and delete the styles. <a href="https://storybook.company.com/latest/?path=/docs/components-progressbar--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css) | 32-35 | 157 | | 🚨 _error_ | 🔗🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>progress-bar</code>. Use <code>DSProgressBar</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-progressbar--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html) | 17-19 | 158 | 159 | </details> 160 | 161 | Coverage audit for DSProgressBar component. Matching classes: progress-bar, loading-bar, legacy-progress 162 | 163 | ### Usage coverage for DSInput component (Angular Design System Coverage) 164 | 165 | <details> 166 | <summary>🟥 <b>1 class found</b> (score: 0)</summary> 167 | 168 | #### Issues 169 | 170 | | Severity | Message | Source file | Line(s) | 171 | | :--------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: | 172 | | 🚨 _error_ | ✏️🔲 Element <code>input</code> in attribute <code>class</code> uses deprecated class <code>form-control</code>. Use <code>DSInput</code> instead. <a href="https://storybook.company.com/latest/?path=/docs/components-input--overview" target="_blank">Learn more</a>. | [`plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert-tooltip-input.component.ts`](../../../../plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert-tooltip-input.component.ts) | 10 | 173 | 174 | </details> 175 | 176 | Coverage audit for DSInput component. Matching classes: input, form-control, legacy-input 177 | 178 | ### Usage coverage for DSAccordion component (Angular Design System Coverage) 179 | 180 | 🟩 **0 classes found** (score: 100) 181 | 182 | Coverage audit for DSAccordion component. Matching classes: accordion, collapse-panel, legacy-accordion 183 | 184 | ### Usage coverage for DSCard component (Angular Design System Coverage) 185 | 186 | 🟩 **0 classes found** (score: 100) 187 | 188 | Coverage audit for DSCard component. Matching classes: card, legacy-card, custom-card 189 | 190 | ### Usage coverage for DSNavbar component (Angular Design System Coverage) 191 | 192 | 🟩 **0 classes found** (score: 100) 193 | 194 | Coverage audit for DSNavbar component. Matching classes: navbar, navigation, legacy-navbar 195 | 196 | ### Usage coverage for DSSlider component (Angular Design System Coverage) 197 | 198 | 🟩 **0 classes found** (score: 100) 199 | 200 | Coverage audit for DSSlider component. Matching classes: slider, range-slider, legacy-slider 201 | 202 | ### Usage coverage for DSTabsModule component (Angular Design System Coverage) 203 | 204 | 🟩 **0 classes found** (score: 100) 205 | 206 | Coverage audit for DSTabsModule component. Matching classes: ms-tab-bar, legacy-tabs, custom-tabs 207 | 208 | ## About 209 | 210 | Report was created by [Code PushUp](https://github.com/code-pushup/cli#readme) on Mon, Feb 10, 2025, 11:48 PM GMT+1. 211 | 212 | | Plugin | Audits | Version | Duration | 213 | | :----------------------------- | :----: | :-----: | -------: | 214 | | Angular Design System Coverage | 13 | | 366 ms | 215 | 216 | | Commit | Version | Duration | Plugins | Categories | Audits | 217 | | :--------------------------------------------- | :------: | -------: | :-----: | :--------: | :----: | 218 | | wip (523fdf3354f4fdb952ab324ac92f7e0f3cd61957) | `0.57.0` | 418 ms | 1 | 1 | 13 | 219 | 220 | --- 221 | 222 | Made with ❤ by [Code PushUp](https://github.com/code-pushup/cli#readme) 223 | ``` -------------------------------------------------------------------------------- /packages/shared/ds-component-coverage/docs/examples/report.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "commit": { 3 | "hash": "523fdf3354f4fdb952ab324ac92f7e0f3cd61957", 4 | "message": "wip", 5 | "date": "2025-02-10T22:11:27.000Z", 6 | "author": "Michael" 7 | }, 8 | "packageName": "@push-based/core", 9 | "version": "0.57.0", 10 | "date": "2025-02-10T22:48:50.259Z", 11 | "duration": 418, 12 | "categories": [ 13 | { 14 | "slug": "design-system-coverage", 15 | "refs": [ 16 | { 17 | "slug": "coverage-dsbutton", 18 | "weight": 1, 19 | "type": "audit", 20 | "plugin": "ds-component-coverage" 21 | }, 22 | { 23 | "slug": "coverage-dstabsmodule", 24 | "weight": 1, 25 | "type": "audit", 26 | "plugin": "ds-component-coverage" 27 | }, 28 | { 29 | "slug": "coverage-dscard", 30 | "weight": 1, 31 | "type": "audit", 32 | "plugin": "ds-component-coverage" 33 | }, 34 | { 35 | "slug": "coverage-dsmodal", 36 | "weight": 1, 37 | "type": "audit", 38 | "plugin": "ds-component-coverage" 39 | }, 40 | { 41 | "slug": "coverage-dsinput", 42 | "weight": 1, 43 | "type": "audit", 44 | "plugin": "ds-component-coverage" 45 | }, 46 | { 47 | "slug": "coverage-dsdropdown", 48 | "weight": 1, 49 | "type": "audit", 50 | "plugin": "ds-component-coverage" 51 | }, 52 | { 53 | "slug": "coverage-dsaccordion", 54 | "weight": 1, 55 | "type": "audit", 56 | "plugin": "ds-component-coverage" 57 | }, 58 | { 59 | "slug": "coverage-dsalert", 60 | "weight": 1, 61 | "type": "audit", 62 | "plugin": "ds-component-coverage" 63 | }, 64 | { 65 | "slug": "coverage-dstooltip", 66 | "weight": 1, 67 | "type": "audit", 68 | "plugin": "ds-component-coverage" 69 | }, 70 | { 71 | "slug": "coverage-dsbreadcrumb", 72 | "weight": 1, 73 | "type": "audit", 74 | "plugin": "ds-component-coverage" 75 | }, 76 | { 77 | "slug": "coverage-dsprogressbar", 78 | "weight": 1, 79 | "type": "audit", 80 | "plugin": "ds-component-coverage" 81 | }, 82 | { 83 | "slug": "coverage-dsslider", 84 | "weight": 1, 85 | "type": "audit", 86 | "plugin": "ds-component-coverage" 87 | }, 88 | { 89 | "slug": "coverage-dsnavbar", 90 | "weight": 1, 91 | "type": "audit", 92 | "plugin": "ds-component-coverage" 93 | } 94 | ], 95 | "title": "Design System Coverage", 96 | "description": "Usage of design system components" 97 | } 98 | ], 99 | "plugins": [ 100 | { 101 | "title": "Angular Design System Coverage", 102 | "slug": "ds-component-coverage", 103 | "icon": "angular", 104 | "date": "2025-02-10T22:48:50.286Z", 105 | "duration": 366, 106 | "audits": [ 107 | { 108 | "slug": "coverage-dsbutton", 109 | "displayValue": "7 classes found", 110 | "value": 7, 111 | "score": 0, 112 | "details": { 113 | "issues": [ 114 | { 115 | "message": "✏️🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn</code>. Use <code>DSButton</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-button--overview\" target=\"_blank\">Learn more</a>.", 116 | "severity": "error", 117 | "source": { 118 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts", 119 | "position": { 120 | "startLine": 10, 121 | "startColumn": 4, 122 | "endLine": 10, 123 | "endColumn": 55 124 | } 125 | } 126 | }, 127 | { 128 | "message": "✏️🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn-primary</code>. Use <code>DSButton</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-button--overview\" target=\"_blank\">Learn more</a>.", 129 | "severity": "error", 130 | "source": { 131 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts", 132 | "position": { 133 | "startLine": 10, 134 | "startColumn": 4, 135 | "endLine": 10, 136 | "endColumn": 55 137 | } 138 | } 139 | }, 140 | { 141 | "message": "✏️🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn</code>. Use <code>DSButton</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-button--overview\" target=\"_blank\">Learn more</a>.", 142 | "severity": "error", 143 | "source": { 144 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-button-dropdown.component.ts", 145 | "position": { 146 | "startLine": 6, 147 | "startColumn": 4, 148 | "endLine": 6, 149 | "endColumn": 53 150 | } 151 | } 152 | }, 153 | { 154 | "message": "✏️🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn-primary</code>. Use <code>DSButton</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-button--overview\" target=\"_blank\">Learn more</a>.", 155 | "severity": "error", 156 | "source": { 157 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-button-dropdown.component.ts", 158 | "position": { 159 | "startLine": 6, 160 | "startColumn": 4, 161 | "endLine": 6, 162 | "endColumn": 53 163 | } 164 | } 165 | }, 166 | { 167 | "message": "🔗🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn</code>. Use <code>DSButton</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-button--overview\" target=\"_blank\">Learn more</a>.", 168 | "severity": "error", 169 | "source": { 170 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html", 171 | "position": { 172 | "startLine": 6, 173 | "endLine": 6, 174 | "endColumn": 54 175 | } 176 | } 177 | }, 178 | { 179 | "message": "🔗🔲 Element <code>button</code> in attribute <code>class</code> uses deprecated class <code>btn-primary</code>. Use <code>DSButton</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-button--overview\" target=\"_blank\">Learn more</a>.", 180 | "severity": "error", 181 | "source": { 182 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html", 183 | "position": { 184 | "startLine": 6, 185 | "endLine": 6, 186 | "endColumn": 54 187 | } 188 | } 189 | }, 190 | { 191 | "message": "🔗🎨 ️ The selector's class <code>btn</code> is deprecated. Use <code>DSButton</code> and delete the styles. <a href=\"https://storybook.company.com/latest/?path=/docs/components-button--overview\" target=\"_blank\">Learn more</a>.", 192 | "severity": "error", 193 | "source": { 194 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css", 195 | "position": { 196 | "startLine": 7, 197 | "startColumn": 1, 198 | "endLine": 14, 199 | "endColumn": 1 200 | } 201 | } 202 | } 203 | ] 204 | }, 205 | "title": "Usage coverage for DSButton component", 206 | "description": "Coverage audit for DSButton component. Matching classes: btn, btn-primary, legacy-button" 207 | }, 208 | { 209 | "slug": "coverage-dstabsmodule", 210 | "displayValue": "0 classes found", 211 | "value": 0, 212 | "score": 1, 213 | "details": { 214 | "issues": [] 215 | }, 216 | "title": "Usage coverage for DSTabsModule component", 217 | "description": "Coverage audit for DSTabsModule component. Matching classes: ms-tab-bar, legacy-tabs, custom-tabs" 218 | }, 219 | { 220 | "slug": "coverage-dscard", 221 | "displayValue": "0 classes found", 222 | "value": 0, 223 | "score": 1, 224 | "details": { 225 | "issues": [] 226 | }, 227 | "title": "Usage coverage for DSCard component", 228 | "description": "Coverage audit for DSCard component. Matching classes: card, legacy-card, custom-card" 229 | }, 230 | { 231 | "slug": "coverage-dsmodal", 232 | "displayValue": "4 classes found", 233 | "value": 4, 234 | "score": 0, 235 | "details": { 236 | "issues": [ 237 | { 238 | "message": "✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>modal</code>. Use <code>DSModal</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-modal--overview\" target=\"_blank\">Learn more</a>.", 239 | "severity": "error", 240 | "source": { 241 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-modal-progress.component.ts", 242 | "position": { 243 | "startLine": 6, 244 | "startColumn": 4, 245 | "endLine": 11, 246 | "endColumn": 10 247 | } 248 | } 249 | }, 250 | { 251 | "message": "✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>modal</code>. Use <code>DSModal</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-modal--overview\" target=\"_blank\">Learn more</a>.", 252 | "severity": "error", 253 | "source": { 254 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts", 255 | "position": { 256 | "startLine": 18, 257 | "startColumn": 4, 258 | "endLine": 23, 259 | "endColumn": 10 260 | } 261 | } 262 | }, 263 | { 264 | "message": "🔗🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>modal</code>. Use <code>DSModal</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-modal--overview\" target=\"_blank\">Learn more</a>.", 265 | "severity": "error", 266 | "source": { 267 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html", 268 | "position": { 269 | "startLine": 9, 270 | "endLine": 14, 271 | "endColumn": 6 272 | } 273 | } 274 | }, 275 | { 276 | "message": "🔗🎨 ️ The selector's class <code>modal</code> is deprecated. Use <code>DSModal</code> and delete the styles. <a href=\"https://storybook.company.com/latest/?path=/docs/components-modal--overview\" target=\"_blank\">Learn more</a>.", 277 | "severity": "error", 278 | "source": { 279 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css", 280 | "position": { 281 | "startLine": 17, 282 | "startColumn": 1, 283 | "endLine": 25, 284 | "endColumn": 1 285 | } 286 | } 287 | } 288 | ] 289 | }, 290 | "title": "Usage coverage for DSModal component", 291 | "description": "Coverage audit for DSModal component. Matching classes: modal, popup, legacy-dialog" 292 | }, 293 | { 294 | "slug": "coverage-dsinput", 295 | "displayValue": "1 class found", 296 | "value": 1, 297 | "score": 0, 298 | "details": { 299 | "issues": [ 300 | { 301 | "message": "✏️🔲 Element <code>input</code> in attribute <code>class</code> uses deprecated class <code>form-control</code>. Use <code>DSInput</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-input--overview\" target=\"_blank\">Learn more</a>.", 302 | "severity": "error", 303 | "source": { 304 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert-tooltip-input.component.ts", 305 | "position": { 306 | "startLine": 10, 307 | "startColumn": 4, 308 | "endLine": 10, 309 | "endColumn": 64 310 | } 311 | } 312 | } 313 | ] 314 | }, 315 | "title": "Usage coverage for DSInput component", 316 | "description": "Coverage audit for DSInput component. Matching classes: input, form-control, legacy-input" 317 | }, 318 | { 319 | "slug": "coverage-dsdropdown", 320 | "displayValue": "4 classes found", 321 | "value": 4, 322 | "score": 0, 323 | "details": { 324 | "issues": [ 325 | { 326 | "message": "✏️🔲 Element <code>select</code> in attribute <code>class</code> uses deprecated class <code>dropdown</code>. Use <code>DSDropdown</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-dropdown--overview\" target=\"_blank\">Learn more</a>.", 327 | "severity": "error", 328 | "source": { 329 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts", 330 | "position": { 331 | "startLine": 37, 332 | "startColumn": 4, 333 | "endLine": 40, 334 | "endColumn": 13 335 | } 336 | } 337 | }, 338 | { 339 | "message": "✏️🔲 Element <code>select</code> in attribute <code>class</code> uses deprecated class <code>dropdown</code>. Use <code>DSDropdown</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-dropdown--overview\" target=\"_blank\">Learn more</a>.", 340 | "severity": "error", 341 | "source": { 342 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-button-dropdown.component.ts", 343 | "position": { 344 | "startLine": 7, 345 | "startColumn": 4, 346 | "endLine": 10, 347 | "endColumn": 13 348 | } 349 | } 350 | }, 351 | { 352 | "message": "🔗🔲 Element <code>select</code> in attribute <code>class</code> uses deprecated class <code>dropdown</code>. Use <code>DSDropdown</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-dropdown--overview\" target=\"_blank\">Learn more</a>.", 353 | "severity": "error", 354 | "source": { 355 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html", 356 | "position": { 357 | "startLine": 25, 358 | "endLine": 28, 359 | "endColumn": 9 360 | } 361 | } 362 | }, 363 | { 364 | "message": "🔗🎨 ️ The selector's class <code>dropdown</code> is deprecated. Use <code>DSDropdown</code> and delete the styles. <a href=\"https://storybook.company.com/latest/?path=/docs/components-dropdown--overview\" target=\"_blank\">Learn more</a>.", 365 | "severity": "error", 366 | "source": { 367 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css", 368 | "position": { 369 | "startLine": 50, 370 | "startColumn": 1, 371 | "endLine": 54, 372 | "endColumn": 1 373 | } 374 | } 375 | } 376 | ] 377 | }, 378 | "title": "Usage coverage for DSDropdown component", 379 | "description": "Coverage audit for DSDropdown component. Matching classes: dropdown, legacy-dropdown, custom-dropdown" 380 | }, 381 | { 382 | "slug": "coverage-dsaccordion", 383 | "displayValue": "0 classes found", 384 | "value": 0, 385 | "score": 1, 386 | "details": { 387 | "issues": [] 388 | }, 389 | "title": "Usage coverage for DSAccordion component", 390 | "description": "Coverage audit for DSAccordion component. Matching classes: accordion, collapse-panel, legacy-accordion" 391 | }, 392 | { 393 | "slug": "coverage-dsalert", 394 | "displayValue": "6 classes found", 395 | "value": 6, 396 | "score": 0, 397 | "details": { 398 | "issues": [ 399 | { 400 | "message": "✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>alert</code>. Use <code>DSAlert</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-alert--overview\" target=\"_blank\">Learn more</a>.", 401 | "severity": "error", 402 | "source": { 403 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts", 404 | "position": { 405 | "startLine": 46, 406 | "startColumn": 4, 407 | "endLine": 46, 408 | "endColumn": 51 409 | } 410 | } 411 | }, 412 | { 413 | "message": "✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>alert</code>. Use <code>DSAlert</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-alert--overview\" target=\"_blank\">Learn more</a>.", 414 | "severity": "error", 415 | "source": { 416 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert-tooltip-input.component.ts", 417 | "position": { 418 | "startLine": 6, 419 | "startColumn": 4, 420 | "endLine": 6, 421 | "endColumn": 67 422 | } 423 | } 424 | }, 425 | { 426 | "message": "✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>alert</code>. Use <code>DSAlert</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-alert--overview\" target=\"_blank\">Learn more</a>.", 427 | "severity": "error", 428 | "source": { 429 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert.component.ts", 430 | "position": { 431 | "startLine": 5, 432 | "startColumn": 1, 433 | "endLine": 5, 434 | "endColumn": 62 435 | } 436 | } 437 | }, 438 | { 439 | "message": "✏️🎨 ️ The selector's class <code>alert</code> is deprecated. Use <code>DSAlert</code> and delete the styles. <a href=\"https://storybook.company.com/latest/?path=/docs/components-alert--overview\" target=\"_blank\">Learn more</a>.", 440 | "severity": "error", 441 | "source": { 442 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert.component.ts", 443 | "position": { 444 | "startLine": 7, 445 | "startColumn": 1, 446 | "endLine": 10, 447 | "endColumn": 7 448 | } 449 | } 450 | }, 451 | { 452 | "message": "🔗🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>alert</code>. Use <code>DSAlert</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-alert--overview\" target=\"_blank\">Learn more</a>.", 453 | "severity": "error", 454 | "source": { 455 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html", 456 | "position": { 457 | "startLine": 22, 458 | "endLine": 22, 459 | "endColumn": 61 460 | } 461 | } 462 | }, 463 | { 464 | "message": "🔗🎨 ️ The selector's class <code>alert</code> is deprecated. Use <code>DSAlert</code> and delete the styles. <a href=\"https://storybook.company.com/latest/?path=/docs/components-alert--overview\" target=\"_blank\">Learn more</a>.", 465 | "severity": "error", 466 | "source": { 467 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css", 468 | "position": { 469 | "startLine": 43, 470 | "startColumn": 1, 471 | "endLine": 47, 472 | "endColumn": 1 473 | } 474 | } 475 | } 476 | ] 477 | }, 478 | "title": "Usage coverage for DSAlert component", 479 | "description": "Coverage audit for DSAlert component. Matching classes: alert, notification, legacy-alert" 480 | }, 481 | { 482 | "slug": "coverage-dstooltip", 483 | "displayValue": "5 classes found", 484 | "value": 5, 485 | "score": 0, 486 | "details": { 487 | "issues": [ 488 | { 489 | "message": "✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>tooltip</code>. Use <code>DSTooltip</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-tooltip--overview\" target=\"_blank\">Learn more</a>.", 490 | "severity": "error", 491 | "source": { 492 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts", 493 | "position": { 494 | "startLine": 52, 495 | "startColumn": 4, 496 | "endLine": 52, 497 | "endColumn": 42 498 | } 499 | } 500 | }, 501 | { 502 | "message": "✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>tooltip</code>. Use <code>DSTooltip</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-tooltip--overview\" target=\"_blank\">Learn more</a>.", 503 | "severity": "error", 504 | "source": { 505 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-alert-tooltip-input.component.ts", 506 | "position": { 507 | "startLine": 8, 508 | "startColumn": 4, 509 | "endLine": 8, 510 | "endColumn": 45 511 | } 512 | } 513 | }, 514 | { 515 | "message": "🔗🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>tooltip</code>. Use <code>DSTooltip</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-tooltip--overview\" target=\"_blank\">Learn more</a>.", 516 | "severity": "error", 517 | "source": { 518 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html", 519 | "position": { 520 | "startLine": 31, 521 | "endLine": 31, 522 | "endColumn": 49 523 | } 524 | } 525 | }, 526 | { 527 | "message": "🔗🎨 ️ The selector's class <code>tooltip</code> is deprecated. Use <code>DSTooltip</code> and delete the styles. <a href=\"https://storybook.company.com/latest/?path=/docs/components-tooltip--overview\" target=\"_blank\">Learn more</a>.", 528 | "severity": "error", 529 | "source": { 530 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css", 531 | "position": { 532 | "startLine": 57, 533 | "startColumn": 1, 534 | "endLine": 61, 535 | "endColumn": 1 536 | } 537 | } 538 | }, 539 | { 540 | "message": "🔗🎨 ️ The selector's class <code>tooltip</code> is deprecated. Use <code>DSTooltip</code> and delete the styles. <a href=\"https://storybook.company.com/latest/?path=/docs/components-tooltip--overview\" target=\"_blank\">Learn more</a>.", 541 | "severity": "error", 542 | "source": { 543 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css", 544 | "position": { 545 | "startLine": 63, 546 | "startColumn": 1, 547 | "endLine": 73, 548 | "endColumn": 1 549 | } 550 | } 551 | } 552 | ] 553 | }, 554 | "title": "Usage coverage for DSTooltip component", 555 | "description": "Coverage audit for DSTooltip component. Matching classes: tooltip, legacy-tooltip, info-bubble" 556 | }, 557 | { 558 | "slug": "coverage-dsbreadcrumb", 559 | "displayValue": "4 classes found", 560 | "value": 4, 561 | "score": 0, 562 | "details": { 563 | "issues": [ 564 | { 565 | "message": "✏️🔲 Element <code>nav</code> in attribute <code>class</code> uses deprecated class <code>breadcrumb</code>. Use <code>DSBreadcrumb</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-breadcrumb--overview\" target=\"_blank\">Learn more</a>.", 566 | "severity": "error", 567 | "source": { 568 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts", 569 | "position": { 570 | "startLine": 62, 571 | "startColumn": 4, 572 | "endLine": 64, 573 | "endColumn": 10 574 | } 575 | } 576 | }, 577 | { 578 | "message": "🔗🔲 Element <code>nav</code> in attribute <code>class</code> uses deprecated class <code>breadcrumb</code>. Use <code>DSBreadcrumb</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-breadcrumb--overview\" target=\"_blank\">Learn more</a>.", 579 | "severity": "error", 580 | "source": { 581 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html", 582 | "position": { 583 | "startLine": 34, 584 | "endLine": 36, 585 | "endColumn": 6 586 | } 587 | } 588 | }, 589 | { 590 | "message": "🔗🎨 ️ The selector's class <code>breadcrumb</code> is deprecated. Use <code>DSBreadcrumb</code> and delete the styles. <a href=\"https://storybook.company.com/latest/?path=/docs/components-breadcrumb--overview\" target=\"_blank\">Learn more</a>.", 591 | "severity": "error", 592 | "source": { 593 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css", 594 | "position": { 595 | "startLine": 76, 596 | "startColumn": 1, 597 | "endLine": 79, 598 | "endColumn": 1 599 | } 600 | } 601 | }, 602 | { 603 | "message": "🔗🎨 ️ The selector's class <code>breadcrumb</code> is deprecated. Use <code>DSBreadcrumb</code> and delete the styles. <a href=\"https://storybook.company.com/latest/?path=/docs/components-breadcrumb--overview\" target=\"_blank\">Learn more</a>.", 604 | "severity": "error", 605 | "source": { 606 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css", 607 | "position": { 608 | "startLine": 81, 609 | "startColumn": 1, 610 | "endLine": 84, 611 | "endColumn": 1 612 | } 613 | } 614 | } 615 | ] 616 | }, 617 | "title": "Usage coverage for DSBreadcrumb component", 618 | "description": "Coverage audit for DSBreadcrumb component. Matching classes: breadcrumb, legacy-breadcrumb, nav-breadcrumb" 619 | }, 620 | { 621 | "slug": "coverage-dsprogressbar", 622 | "displayValue": "4 classes found", 623 | "value": 4, 624 | "score": 0, 625 | "details": { 626 | "issues": [ 627 | { 628 | "message": "✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>progress-bar</code>. Use <code>DSProgressBar</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-progressbar--overview\" target=\"_blank\">Learn more</a>.", 629 | "severity": "error", 630 | "source": { 631 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-modal-progress.component.ts", 632 | "position": { 633 | "startLine": 13, 634 | "startColumn": 4, 635 | "endLine": 15, 636 | "endColumn": 10 637 | } 638 | } 639 | }, 640 | { 641 | "message": "✏️🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>progress-bar</code>. Use <code>DSProgressBar</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-progressbar--overview\" target=\"_blank\">Learn more</a>.", 642 | "severity": "error", 643 | "source": { 644 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/bad-mixed.component.ts", 645 | "position": { 646 | "startLine": 29, 647 | "startColumn": 4, 648 | "endLine": 31, 649 | "endColumn": 10 650 | } 651 | } 652 | }, 653 | { 654 | "message": "🔗🔲 Element <code>div</code> in attribute <code>class</code> uses deprecated class <code>progress-bar</code>. Use <code>DSProgressBar</code> instead. <a href=\"https://storybook.company.com/latest/?path=/docs/components-progressbar--overview\" target=\"_blank\">Learn more</a>.", 655 | "severity": "error", 656 | "source": { 657 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.html", 658 | "position": { 659 | "startLine": 17, 660 | "endLine": 19, 661 | "endColumn": 6 662 | } 663 | } 664 | }, 665 | { 666 | "message": "🔗🎨 ️ The selector's class <code>progress-bar</code> is deprecated. Use <code>DSProgressBar</code> and delete the styles. <a href=\"https://storybook.company.com/latest/?path=/docs/components-progressbar--overview\" target=\"_blank\">Learn more</a>.", 667 | "severity": "error", 668 | "source": { 669 | "file": "plugins/ds-component-coverage/mocks/fixtures/coverage-audit/demo/mixed-external-assets.component.css", 670 | "position": { 671 | "startLine": 32, 672 | "startColumn": 1, 673 | "endLine": 35, 674 | "endColumn": 1 675 | } 676 | } 677 | } 678 | ] 679 | }, 680 | "title": "Usage coverage for DSProgressBar component", 681 | "description": "Coverage audit for DSProgressBar component. Matching classes: progress-bar, loading-bar, legacy-progress" 682 | }, 683 | { 684 | "slug": "coverage-dsslider", 685 | "displayValue": "0 classes found", 686 | "value": 0, 687 | "score": 1, 688 | "details": { 689 | "issues": [] 690 | }, 691 | "title": "Usage coverage for DSSlider component", 692 | "description": "Coverage audit for DSSlider component. Matching classes: slider, range-slider, legacy-slider" 693 | }, 694 | { 695 | "slug": "coverage-dsnavbar", 696 | "displayValue": "0 classes found", 697 | "value": 0, 698 | "score": 1, 699 | "details": { 700 | "issues": [] 701 | }, 702 | "title": "Usage coverage for DSNavbar component", 703 | "description": "Coverage audit for DSNavbar component. Matching classes: navbar, navigation, legacy-navbar" 704 | } 705 | ], 706 | "description": "A plugin to measure and assert usage of styles in an Angular project." 707 | } 708 | ] 709 | } 710 | ```