Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/workflows/actions/setup-node
- uses: ./.github/workflows/actions/build-packages
# Yarn's node-modules linker tolerates peer conflicts that npm rejects, so the built packages
# have to be resolved with npm to catch an ERESOLVE before it reaches consumers.
- run: yarn run check-npm-resolution
- uses: ./.github/workflows/actions/build-docs
1 change: 1 addition & 0 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ jobs:
yarn run prettier
yarn run stylelint --max-warnings=0
yarn run eslint --max-warnings=0
yarn run check-peer-deps
3 changes: 3 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
- name: Build packages
uses: ./.github/workflows/actions/build-packages

- name: Check npm can resolve the packages
run: yarn run check-npm-resolution

- name: Publish package
run: |
rm -rf .npmrc && echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN_KOOBIQ" > ~/.npmrc
Expand Down
23 changes: 22 additions & 1 deletion docs/guides/installation.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,30 @@ ng add @koobiq/components
Manual installation:

```bash
npm install @koobiq/components @koobiq/icons @koobiq/design-tokens @koobiq/angular-luxon-adapter @koobiq/date-adapter @koobiq/date-formatter luxon
npm install @koobiq/components @angular/cdk @angular/animations overlayscrollbars @koobiq/icons @koobiq/design-tokens @koobiq/angular-luxon-adapter @koobiq/date-adapter @koobiq/date-formatter luxon
```

`@koobiq/angular-luxon-adapter` (or `@koobiq/angular-moment-adapter`) is only needed if you use the
date components — [datepicker](/en/components/datepicker), [timepicker](/en/components/timepicker) or
[filter-bar](/en/components/filter-bar). Install `marked` if you use
[markdown](/en/components/markdown), and `highlight.js` if you use
[code-block](/en/components/code-block).

### Setting up animations

The components use Angular animations, so the application must provide them:

```typescript
import { provideAnimations } from '@angular/platform-browser/animations';

bootstrapApplication(AppComponent, {
providers: [provideAnimations()]
});
```

Without this provider, opening a component that animates — dropdown, select, tooltip, toast,
datepicker — fails with `NG05105: Unexpected synthetic property @state found`.

### Setting up styles

After installation, you need to include the library styles. Add the following files to the `styles` array in your `angular.json` file:
Expand Down
23 changes: 22 additions & 1 deletion docs/guides/installation.ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,30 @@ ng add @koobiq/components
Ручная установка:

```bash
npm install @koobiq/components @koobiq/icons @koobiq/design-tokens @koobiq/angular-luxon-adapter @koobiq/date-adapter @koobiq/date-formatter luxon
npm install @koobiq/components @angular/cdk @angular/animations overlayscrollbars @koobiq/icons @koobiq/design-tokens @koobiq/angular-luxon-adapter @koobiq/date-adapter @koobiq/date-formatter luxon
```

`@koobiq/angular-luxon-adapter` (или `@koobiq/angular-moment-adapter`) нужен только при использовании
компонентов для работы с датами — [datepicker](/ru/components/datepicker),
[timepicker](/ru/components/timepicker) или [filter-bar](/ru/components/filter-bar). Установите
`marked`, если используете [markdown](/ru/components/markdown), и `highlight.js`, если используете
[code-block](/ru/components/code-block).

### Настройка анимаций

Компоненты используют анимации Angular, поэтому приложение должно их предоставить:

```typescript
import { provideAnimations } from '@angular/platform-browser/animations';

bootstrapApplication(AppComponent, {
providers: [provideAnimations()]
});
```

Без этого провайдера открытие компонента с анимацией — dropdown, select, tooltip, toast,
datepicker — завершится ошибкой `NG05105: Unexpected synthetic property @state found`.

### Настройка стилей

После установки необходимо подключить стили библиотеки. Добавьте следующие файлы в массив `styles` вашего файла `angular.json`:
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"@types/merge2": "^0.3.30",
"@types/node": "^24.10.4",
"@types/nunjucks": "^3.2.1",
"@types/semver": "^7.7.1",
"@types/spdx-satisfies": "^0.1.2",
"angular-eslint": "^20.7.0",
"autoprefixer": "^10.5.2",
Expand Down Expand Up @@ -137,6 +138,7 @@
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.37.0",
"sass": "^1.93.3",
"semver": "^7.8.1",
"spdx-satisfies": "^5.0.1",
"style-dictionary": "^3.7.1",
"stylelint": "^17.14.1",
Expand Down Expand Up @@ -296,6 +298,8 @@
"approve-api": "ts-node --project tools/api-extractor/tsconfig.json tools/api-extractor/api-extractor.ts",
"check-api": "yarn run approve-api onlyCheck",
"-----LINTERS-----": "----------------------------------------------------------------------------------------",
"check-peer-deps": "ts-node --project tools/check-peer-deps/tsconfig.json tools/check-peer-deps",
"check-npm-resolution": "ts-node --project tools/check-npm-resolution/tsconfig.json tools/check-npm-resolution",
"eslint": "eslint .",
"eslint:fix": "yarn run eslint --fix",
"stylelint": "stylelint '**/*.{css,scss}'",
Expand Down
2 changes: 1 addition & 1 deletion packages/angular-luxon-adapter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"license": "MIT",
"peerDependencies": {
"@koobiq/luxon-date-adapter": "^3.1.4",
"@koobiq/components": "{{VERSION}}"
"@koobiq/components": "^{{VERSION}}"
},
"dependencies": {
"tslib": "^2.6.2"
Expand Down
2 changes: 1 addition & 1 deletion packages/angular-moment-adapter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"license": "MIT",
"peerDependencies": {
"@koobiq/moment-date-adapter": "^3.1.4",
"@koobiq/components": "{{VERSION}}"
"@koobiq/components": "^{{VERSION}}"
},
"dependencies": {
"tslib": "^2.6.2"
Expand Down
2 changes: 1 addition & 1 deletion packages/components-experimental/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@angular/cdk": "{{NG_VERSION}}",
"@angular/core": "{{NG_VERSION}}",
"@angular/forms": "{{NG_VERSION}}",
"@koobiq/components": "{{VERSION}}"
"@koobiq/components": "^{{VERSION}}"
},
"dependencies": {
"tslib": "^2.6.2"
Expand Down
33 changes: 29 additions & 4 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,38 @@
"peerDependencies": {
"@angular/animations": "{{NG_VERSION}}",
"@angular/cdk": "{{NG_VERSION}}",
"@angular/common": "{{NG_VERSION}}",
"@angular/core": "{{NG_VERSION}}",
"@angular/forms": "{{NG_VERSION}}",
"@koobiq/angular-moment-adapter": "{{VERSION}}",
"@koobiq/angular-luxon-adapter": "{{VERSION}}",
"@angular/platform-browser": "{{NG_VERSION}}",
"@angular/router": "{{NG_VERSION}}",
"@koobiq/angular-moment-adapter": "^{{VERSION}}",
"@koobiq/angular-luxon-adapter": "^{{VERSION}}",
"@koobiq/date-adapter": "^3.0.0",
"@koobiq/date-formatter": "^3.2.3",
"@koobiq/icons": "^11.1.3",
"@koobiq/design-tokens": "^3.14.0"
"@koobiq/icons": ">=11.1.3 <13.0.0",
"@koobiq/design-tokens": "^3.14.0",
"highlight.js": "^11.11.1",
"marked": "^17.0.0",
"overlayscrollbars": "^2.7.3",
"rxjs": "^6.5.3 || ^7.4.0"
},
"peerDependenciesMeta": {
"@angular/router": {
"optional": true
},
"@koobiq/angular-moment-adapter": {
"optional": true
},
"@koobiq/angular-luxon-adapter": {
"optional": true
},
"highlight.js": {
"optional": true
},
"marked": {
"optional": true
}
},
"dependencies": {
"tslib": "^2.6.2"
Expand Down
11 changes: 8 additions & 3 deletions packages/docs-examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@
],
"license": "MIT",
"peerDependencies": {
"@angular/animations": "{{NG_VERSION}}",
"@angular/cdk": "{{NG_VERSION}}",
"@angular/core": "{{NG_VERSION}}",
"@angular/common": "{{NG_VERSION}}",
"@koobiq/components": "{{VERSION}}",
"@koobiq/angular-moment-adapter": "{{VERSION}}",
"@koobiq/angular-luxon-adapter": "{{VERSION}}"
"@angular/forms": "{{NG_VERSION}}",
"@angular/platform-browser": "{{NG_VERSION}}",
"@angular/router": "{{NG_VERSION}}",
"@koobiq/components": "^{{VERSION}}",
"@koobiq/angular-moment-adapter": "^{{VERSION}}",
"@koobiq/angular-luxon-adapter": "^{{VERSION}}",
"highlight.js": "^11.11.1"
},
"dependencies": {
"tslib": "^2.6.2"
Expand Down
4 changes: 3 additions & 1 deletion packages/schematics/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,16 @@ module.exports = [
clean(),
replace({
preventAssignment: true,
'VERSIONS.ANGULAR_ANIMATIONS': version(pkg.dependencies['@angular/animations']),
'VERSIONS.ANGULAR_CDK': version(pkg.dependencies['@angular/cdk']),
'VERSIONS.KOOBIQ_CDK': version(pkg.version),
'VERSIONS.KOOBIQ_ANGULAR_LUXON_ADAPTER': version(pkg.version),
'VERSIONS.KOOBIQ_DATE_FORMATTER': version(pkg.dependencies['@koobiq/date-formatter']),
'VERSIONS.KOOBIQ_DATE_ADAPTER': version(pkg.dependencies['@koobiq/date-adapter']),
'VERSIONS.KOOBIQ_DESIGN_TOKENS': version(pkg.devDependencies['@koobiq/design-tokens']),
'VERSIONS.KOOBIQ_ICONS': version(pkg.dependencies['@koobiq/icons']),
'VERSIONS.LUXON': version(pkg.devDependencies.luxon)
'VERSIONS.LUXON': version(pkg.devDependencies.luxon),
'VERSIONS.OVERLAYSCROLLBARS': version(pkg.dependencies.overlayscrollbars)
}),
typescript({
tsconfig: path.join(__dirname, 'tsconfig.rollup.json')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

exports[`ng add '@koobiq/components' should add missing dependencies to 'package.json': after running schematics 1`] = `
{
"@angular/animations": "^0.0.0",
"@angular/cdk": "^0.0.0",
"@angular/common": "^20.3.0",
"@angular/compiler": "^20.3.0",
Expand All @@ -15,6 +16,7 @@ exports[`ng add '@koobiq/components' should add missing dependencies to 'package
"@koobiq/design-tokens": "^0.0.0",
"@koobiq/icons": "^0.0.0",
"luxon": "^0.0.0",
"overlayscrollbars": "^0.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0",
Expand Down
24 changes: 21 additions & 3 deletions packages/schematics/src/ng-add/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics';
import { Rule, SchematicContext, SchematicsException, Tree } from '@angular-devkit/schematics';
import { readWorkspace } from '@schematics/angular/utility';
import { logMessage } from '../utils/messages';
import { addPackageToPackageJson } from '../utils/package-config';
import * as messages from './messages';
import { Schema } from './schema';

const VERSIONS = {
ANGULAR_ANIMATIONS: '^0.0.0',
ANGULAR_CDK: '^0.0.0',
KOOBIQ_ANGULAR_LUXON_ADAPTER: '^0.0.0',
KOOBIQ_DATE_FORMATTER: '^0.0.0',
KOOBIQ_DATE_ADAPTER: '^0.0.0',
KOOBIQ_ICONS: '^0.0.0',
KOOBIQ_DESIGN_TOKENS: '^0.0.0',
LUXON: '^0.0.0'
LUXON: '^0.0.0',
OVERLAYSCROLLBARS: '^0.0.0'
};

/**
* This is executed when `ng add @koobiq/components` is run.
* It installs all dependencies in the 'package.json' and runs 'ng-add-setup-project' schematic.
*/
export default function ngAdd(options: Schema): Rule {
return async (tree: Tree) => {
return async (tree: Tree, context: SchematicContext) => {
const { project } = options;

if (project) {
Expand All @@ -32,12 +35,27 @@ export default function ngAdd(options: Schema): Rule {
}

// Installing dependencies
// `@angular/animations` is a mandatory peer: the components declare `animations: [...]`
// metadata and bind synthetic `[@state]` properties, which throw NG05105 without it.
addPackageToPackageJson(tree, '@angular/animations', VERSIONS.ANGULAR_ANIMATIONS);
addPackageToPackageJson(tree, '@angular/cdk', VERSIONS.ANGULAR_CDK);
addPackageToPackageJson(tree, '@koobiq/angular-luxon-adapter', VERSIONS.KOOBIQ_ANGULAR_LUXON_ADAPTER);
addPackageToPackageJson(tree, '@koobiq/date-formatter', VERSIONS.KOOBIQ_DATE_FORMATTER);
addPackageToPackageJson(tree, '@koobiq/date-adapter', VERSIONS.KOOBIQ_DATE_ADAPTER);
addPackageToPackageJson(tree, '@koobiq/icons', VERSIONS.KOOBIQ_ICONS);
addPackageToPackageJson(tree, '@koobiq/design-tokens', VERSIONS.KOOBIQ_DESIGN_TOKENS);
addPackageToPackageJson(tree, 'luxon', VERSIONS.LUXON);
// `overlayscrollbars` is a mandatory peer too: `@koobiq/components/scrollbar` imports it
// unconditionally, and content-panel, notification-center and app-switcher all pull that in.
addPackageToPackageJson(tree, 'overlayscrollbars', VERSIONS.OVERLAYSCROLLBARS);

// Installing `@angular/animations` only satisfies the peer; the application still has to
// provide the animations module itself, so point at the one step this schematic cannot do.
logMessage(context.logger, [
'Angular animations have to be provided by the application.',
"Add `provideAnimations()` from '@angular/platform-browser/animations' to the providers",
'of `bootstrapApplication`, otherwise every component that animates (dropdown, select,',
'tooltip, toast, datepicker) fails with NG05105 as soon as it opens.'
]);
};
}
13 changes: 13 additions & 0 deletions packages/schematics/src/ng-add/ng-add.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ describe(`ng add '@koobiq/components'`, () => {
expect(getPackageJsonDependencies(tree)).toMatchSnapshot('after running schematics');
});

it(`should add every mandatory peer of '@koobiq/components'`, async () => {
const tree = await runner.runSchematic('ng-add', {}, appTree);
const dependencies = getPackageJsonDependencies(tree);

// A mandatory peer that `ng add` skips leaves the consumer with a package.json that never
// recorded it, so the app only breaks later, at bundle time, with "Module not found".
// The versions themselves are injected by rollup at build time, so they read as the
// `^0.0.0` source default here — only the presence of the entry is meaningful.
['@angular/animations', '@angular/cdk', 'overlayscrollbars'].forEach((dependency) => {
expect(dependencies[dependency]).toBeDefined();
});
});

it(`should report when specified 'project' is not found`, async () => {
await expect(runner.runSchematic('ng-add', { project: 'test' }, appTree)).rejects.toThrow(
"Unable to find project 'test' in the workspace"
Expand Down
Loading
Loading