mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1866: Anlage Komponenten und Directives + Unit Tests und Stories
Related work items: #5175
This commit is contained in:
committed by
Nino Righi
parent
b21ebac53f
commit
4cf0ce820e
@@ -2,8 +2,11 @@ import type { StorybookConfig } from '@storybook/angular';
|
|||||||
|
|
||||||
const config: StorybookConfig = {
|
const config: StorybookConfig = {
|
||||||
stories: ['../stories/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
|
stories: ['../stories/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
|
||||||
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
|
addons: [
|
||||||
staticDirs: ['../src/assets'],
|
'@storybook/addon-essentials',
|
||||||
|
'@storybook/addon-interactions',
|
||||||
|
'@storybook/addon-docs',
|
||||||
|
],
|
||||||
previewHead: (head) => `
|
previewHead: (head) => `
|
||||||
${head}
|
${head}
|
||||||
<link href="/assets/fonts/fonts.css" rel="stylesheet" />
|
<link href="/assets/fonts/fonts.css" rel="stylesheet" />
|
||||||
|
|||||||
@@ -118,7 +118,19 @@
|
|||||||
"configDir": "apps/isa-app/.storybook",
|
"configDir": "apps/isa-app/.storybook",
|
||||||
"browserTarget": "isa-app:build",
|
"browserTarget": "isa-app:build",
|
||||||
"compodoc": false,
|
"compodoc": false,
|
||||||
"styles": ["apps/isa-app/src/ui.scss", "apps/isa-app/src/styles.scss"]
|
"open": false,
|
||||||
|
"assets": [
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "apps/isa-app/src/assets",
|
||||||
|
"output": "/assets"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"@angular/cdk/overlay-prebuilt.css",
|
||||||
|
"apps/isa-app/src/ui.scss",
|
||||||
|
"apps/isa-app/src/styles.scss"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"ci": {
|
"ci": {
|
||||||
@@ -133,33 +145,18 @@
|
|||||||
"outputDir": "dist/storybook/isa-app",
|
"outputDir": "dist/storybook/isa-app",
|
||||||
"configDir": "apps/isa-app/.storybook",
|
"configDir": "apps/isa-app/.storybook",
|
||||||
"browserTarget": "isa-app:build",
|
"browserTarget": "isa-app:build",
|
||||||
"compodoc": false
|
"compodoc": false,
|
||||||
|
"styles": [
|
||||||
|
"@angular/cdk/overlay-prebuilt.css",
|
||||||
|
"apps/isa-app/src/ui.scss",
|
||||||
|
"apps/isa-app/src/styles.scss"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"ci": {
|
"ci": {
|
||||||
"quiet": true
|
"quiet": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"test-storybook": {
|
|
||||||
"executor": "nx:run-commands",
|
|
||||||
"options": {
|
|
||||||
"command": "test-storybook -c apps/isa-app/.storybook --url=http://localhost:4400"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"static-storybook": {
|
|
||||||
"executor": "@nx/web:file-server",
|
|
||||||
"dependsOn": ["build-storybook"],
|
|
||||||
"options": {
|
|
||||||
"buildTarget": "isa-app:build-storybook",
|
|
||||||
"staticFilePath": "dist/storybook/isa-app",
|
|
||||||
"spa": true
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"ci": {
|
|
||||||
"buildTarget": "isa-app:build-storybook:ci"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
@use '../../../libs/ui/buttons/src/buttons.scss';
|
@use "../../../libs/ui/buttons/src/buttons.scss";
|
||||||
@use '../../../libs/ui/datepicker/src/datepicker.scss';
|
@use "../../../libs/ui/datepicker/src/datepicker.scss";
|
||||||
@use '../../../libs/ui/dialog/src/dialog.scss';
|
@use "../../../libs/ui/dialog/src/dialog.scss";
|
||||||
@use '../../../libs/ui/input-controls/src/input-controls.scss';
|
@use "../../../libs/ui/input-controls/src/input-controls.scss";
|
||||||
@use '../../../libs/ui/menu/src/menu.scss';
|
@use "../../../libs/ui/menu/src/menu.scss";
|
||||||
@use '../../../libs/ui/progress-bar/src/lib/progress-bar.scss';
|
@use "../../../libs/ui/progress-bar/src/lib/progress-bar.scss";
|
||||||
@use '../../../libs/ui/tooltip/src/tooltip.scss';
|
@use "../../../libs/ui/skeleton-loader/src/skeleton-loader.scss";
|
||||||
|
@use "../../../libs/ui/tooltip/src/tooltip.scss";
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import {
|
||||||
|
type Meta,
|
||||||
|
type StoryObj,
|
||||||
|
applicationConfig,
|
||||||
|
argsToTemplate,
|
||||||
|
moduleMetadata,
|
||||||
|
} from '@storybook/angular';
|
||||||
|
import {
|
||||||
|
ProductInfoComponent,
|
||||||
|
ProductInfoItem,
|
||||||
|
ProductInfoOrientation,
|
||||||
|
} from '@isa/remission/shared/product';
|
||||||
|
import { provideProductImageUrl } from '@isa/shared/product-image';
|
||||||
|
import { provideProductRouterLinkBuilder } from '@isa/shared/product-router-link';
|
||||||
|
import { provideRouter } from '@angular/router';
|
||||||
|
|
||||||
|
type ProductInfoInputs = {
|
||||||
|
item: ProductInfoItem;
|
||||||
|
orientation: ProductInfoOrientation;
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta: Meta<ProductInfoInputs> = {
|
||||||
|
component: ProductInfoComponent,
|
||||||
|
title: 'remission/shared/product/ProductInfoComponent',
|
||||||
|
decorators: [
|
||||||
|
applicationConfig({
|
||||||
|
providers: [
|
||||||
|
provideRouter([{ path: ':ean', component: ProductInfoComponent }]),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
moduleMetadata({
|
||||||
|
providers: [
|
||||||
|
provideProductImageUrl('https://produktbilder-test.paragon-data.net'),
|
||||||
|
provideProductRouterLinkBuilder((ean: string) => ean),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
args: {
|
||||||
|
item: {
|
||||||
|
product: {
|
||||||
|
contributors: 'John Doe',
|
||||||
|
ean: '9783742327529',
|
||||||
|
format: 'TB',
|
||||||
|
formatDetail: 'Taschenbuch',
|
||||||
|
name: 'Test Product',
|
||||||
|
},
|
||||||
|
retailPrice: {
|
||||||
|
value: {
|
||||||
|
currency: 'EUR',
|
||||||
|
value: 19.99,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orientation: 'horizontal',
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
item: {
|
||||||
|
control: 'object',
|
||||||
|
description: 'The product item to display.',
|
||||||
|
},
|
||||||
|
orientation: {
|
||||||
|
control: 'select',
|
||||||
|
options: ['horizontal', 'vertical'],
|
||||||
|
description: 'The orientation of the product info display.',
|
||||||
|
table: {
|
||||||
|
defaultValue: {
|
||||||
|
summary: 'horizontal',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: `<remi-product-info ${argsToTemplate(args)}></remi-product-info>`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<ProductInfoInputs>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
item: {
|
||||||
|
product: {
|
||||||
|
contributors: 'John Doe',
|
||||||
|
ean: '9783742327529',
|
||||||
|
format: 'HC',
|
||||||
|
formatDetail: 'Hardcover (gebunden)',
|
||||||
|
name: 'Sample Product',
|
||||||
|
},
|
||||||
|
retailPrice: {
|
||||||
|
value: {
|
||||||
|
currency: 'EUR',
|
||||||
|
value: 29.99,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import {
|
||||||
|
type Meta,
|
||||||
|
type StoryObj,
|
||||||
|
argsToTemplate,
|
||||||
|
moduleMetadata,
|
||||||
|
} from '@storybook/angular';
|
||||||
|
import { ProductStockInfoComponent } from '@isa/remission/shared/product';
|
||||||
|
import { provideProductImageUrl } from '@isa/shared/product-image';
|
||||||
|
import { provideProductRouterLinkBuilder } from '@isa/shared/product-router-link';
|
||||||
|
|
||||||
|
const meta: Meta<ProductStockInfoComponent> = {
|
||||||
|
component: ProductStockInfoComponent,
|
||||||
|
title: 'remission/shared/product/ProductStockInfoComponent',
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
providers: [
|
||||||
|
provideProductImageUrl('https://produktbilder-test.paragon-data.net'),
|
||||||
|
provideProductRouterLinkBuilder((ean: string) => ean),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
args: {
|
||||||
|
stock: 100,
|
||||||
|
stockToRemit: 20,
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
stock: {
|
||||||
|
control: { type: 'number' },
|
||||||
|
description: 'The current stock of the product.',
|
||||||
|
defaultValue: undefined,
|
||||||
|
},
|
||||||
|
stockToRemit: {
|
||||||
|
control: { type: 'number' },
|
||||||
|
description: 'The amount of stock to remit.',
|
||||||
|
defaultValue: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: `<remi-product-stock-info ${argsToTemplate({ info: args })}></remi-product-stock-info>`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<ProductStockInfoComponent>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {},
|
||||||
|
};
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { Meta, argsToTemplate } from '@storybook/angular';
|
||||||
|
import { ProductFormatIconGroup } from '@isa/icons';
|
||||||
|
import { ProductFormatIconComponent } from '@isa/shared/product-foramt';
|
||||||
|
|
||||||
|
type ProductFormatInputs = {
|
||||||
|
format: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = Object.keys(ProductFormatIconGroup).map((key) =>
|
||||||
|
key.toUpperCase(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const meta: Meta<ProductFormatInputs> = {
|
||||||
|
title: 'shared/product-format/ProductFormatIcon',
|
||||||
|
component: ProductFormatIconComponent,
|
||||||
|
argTypes: {
|
||||||
|
format: {
|
||||||
|
control: {
|
||||||
|
type: 'select',
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
description: 'The product format to display the icon for.',
|
||||||
|
defaultValue: options[0],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
format: options[0], // Default value for the product format
|
||||||
|
},
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: `<shared-product-format-icon ${argsToTemplate(args)}></shared-product-format-icon>`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = typeof meta;
|
||||||
|
|
||||||
|
export const Default: Story = {};
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { argsToTemplate, Meta } from '@storybook/angular';
|
||||||
|
import { ProductFormatIconGroup } from '@isa/icons';
|
||||||
|
import { ProductFormatComponent } from '@isa/shared/product-foramt';
|
||||||
|
|
||||||
|
type ProductFormatInputs = {
|
||||||
|
format: string;
|
||||||
|
formatDetail: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = Object.keys(ProductFormatIconGroup).map((key) =>
|
||||||
|
key.toUpperCase(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const meta: Meta<ProductFormatInputs> = {
|
||||||
|
title: 'shared/product-format/ProductFormat',
|
||||||
|
component: ProductFormatComponent,
|
||||||
|
argTypes: {
|
||||||
|
format: {
|
||||||
|
control: {
|
||||||
|
type: 'select',
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
description: 'The product format to display the icon for.',
|
||||||
|
defaultValue: options[0],
|
||||||
|
},
|
||||||
|
formatDetail: {
|
||||||
|
control: {
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
description: 'The detail text for the product format.',
|
||||||
|
defaultValue: 'Default Format Detail',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
format: options[0], // Default value for the product format
|
||||||
|
formatDetail: 'Default Format Detail', // Default value for the format detail
|
||||||
|
},
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: `<shared-product-format ${argsToTemplate(args)}></shared-product-format>`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = typeof meta;
|
||||||
|
|
||||||
|
export const Default: Story = {};
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
import { argsToTemplate, type Meta, type StoryObj, moduleMetadata } from '@storybook/angular';
|
import {
|
||||||
import { ProductImageDirective, provideProductImageUrl } from '@isa/shared/product-image';
|
argsToTemplate,
|
||||||
|
type Meta,
|
||||||
|
type StoryObj,
|
||||||
|
moduleMetadata,
|
||||||
|
} from '@storybook/angular';
|
||||||
|
import {
|
||||||
|
ProductImageDirective,
|
||||||
|
provideProductImageUrl,
|
||||||
|
} from '@isa/shared/product-image';
|
||||||
|
|
||||||
type ProductImageInputs = {
|
type ProductImageInputs = {
|
||||||
ean: string;
|
ean: string;
|
||||||
@@ -13,7 +21,9 @@ const meta: Meta<ProductImageInputs> = {
|
|||||||
decorators: [
|
decorators: [
|
||||||
moduleMetadata({
|
moduleMetadata({
|
||||||
imports: [ProductImageDirective],
|
imports: [ProductImageDirective],
|
||||||
providers: [provideProductImageUrl('https://produktbilder-test.paragon-data.net')],
|
providers: [
|
||||||
|
provideProductImageUrl('https://produktbilder-test.paragon-data.net'),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
argTypes: {
|
argTypes: {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { type Meta, type StoryObj, moduleMetadata } from '@storybook/angular';
|
import { type Meta, type StoryObj, moduleMetadata } from '@storybook/angular';
|
||||||
import { ClientRowComponent, ClientRowImports, ItemRowDataImports } from '@isa/ui/item-rows';
|
import {
|
||||||
|
ClientRowComponent,
|
||||||
|
ClientRowImports,
|
||||||
|
ItemRowDataImports,
|
||||||
|
} from '@isa/ui/item-rows';
|
||||||
|
|
||||||
const meta: Meta<ClientRowComponent> = {
|
const meta: Meta<ClientRowComponent> = {
|
||||||
component: ClientRowComponent,
|
component: ClientRowComponent,
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { type Meta, type StoryObj } from '@storybook/angular';
|
||||||
|
import { SkeletonLoaderDirective } from '@isa/ui/skeleton-loader';
|
||||||
|
|
||||||
|
type SkeletonLoaderDirectiveInputs = {
|
||||||
|
uiSkeletonLoader: boolean;
|
||||||
|
uiSkeletonLoaderWidth?: string;
|
||||||
|
uiSkeletonLoaderHeight?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta: Meta<SkeletonLoaderDirectiveInputs> = {
|
||||||
|
component: SkeletonLoaderDirective,
|
||||||
|
title: 'ui/skeleton-loader/SkeletonLoaderDirective',
|
||||||
|
args: {
|
||||||
|
uiSkeletonLoader: false,
|
||||||
|
uiSkeletonLoaderWidth: '100px',
|
||||||
|
uiSkeletonLoaderHeight: '',
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
uiSkeletonLoader: { control: 'boolean' },
|
||||||
|
uiSkeletonLoaderWidth: { control: 'text' },
|
||||||
|
uiSkeletonLoaderHeight: { control: 'text' },
|
||||||
|
},
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: `<div *uiSkeletonLoader="${args.uiSkeletonLoader}; width: '${args.uiSkeletonLoaderWidth}'; height: '${args.uiSkeletonLoaderHeight}'"> This is my content! </div>`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<SkeletonLoaderDirectiveInputs>;
|
||||||
|
|
||||||
|
export const Default: Story = {};
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { type Meta, type StoryObj } from '@storybook/angular';
|
||||||
|
import { SkeletonLoaderComponent } from '@isa/ui/skeleton-loader';
|
||||||
|
|
||||||
|
const meta: Meta<SkeletonLoaderComponent> = {
|
||||||
|
component: SkeletonLoaderComponent,
|
||||||
|
title: 'ui/skeleton-loader/SkeletonLoader',
|
||||||
|
render: () => ({
|
||||||
|
template: `<ui-skeleton-loader class="w-24"></ui-skeleton-loader>`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<SkeletonLoaderComponent>;
|
||||||
|
|
||||||
|
export const Default: Story = {};
|
||||||
54
eslint.config.js
Normal file
54
eslint.config.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
const nx = require('@nx/eslint-plugin');
|
||||||
|
const eslintConfigPrettier = require('eslint-config-prettier/flat');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
...nx.configs['flat/base'],
|
||||||
|
...nx.configs['flat/typescript'],
|
||||||
|
...nx.configs['flat/javascript'],
|
||||||
|
eslintConfigPrettier,
|
||||||
|
{
|
||||||
|
ignores: ['**/dist'],
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
// rules: {
|
||||||
|
// '@nx/enforce-module-boundaries': [
|
||||||
|
// 'error',
|
||||||
|
// {
|
||||||
|
// enforceBuildableLibDependency: true,
|
||||||
|
// allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?js$'],
|
||||||
|
// depConstraints: [
|
||||||
|
// {
|
||||||
|
// sourceTag: '*',
|
||||||
|
// onlyDependOnLibsWithTags: ['*'],
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {
|
||||||
|
'prettier/prettier': 'error',
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
prettier: require('eslint-plugin-prettier'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.json'],
|
||||||
|
rules: {
|
||||||
|
'@nx/dependency-checks': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
parser: require('jsonc-eslint-parser'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './lib/models';
|
||||||
|
|||||||
4
libs/remission/data-access/src/lib/models/index.ts
Normal file
4
libs/remission/data-access/src/lib/models/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export * from './price-value';
|
||||||
|
export * from './price';
|
||||||
|
export * from './product';
|
||||||
|
export * from './return-item';
|
||||||
6
libs/remission/data-access/src/lib/models/price-value.ts
Normal file
6
libs/remission/data-access/src/lib/models/price-value.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { PriceValueDTO } from '@generated/swagger/inventory-api';
|
||||||
|
|
||||||
|
export interface PriceValue extends PriceValueDTO {
|
||||||
|
value: number;
|
||||||
|
currency: string;
|
||||||
|
}
|
||||||
6
libs/remission/data-access/src/lib/models/price.ts
Normal file
6
libs/remission/data-access/src/lib/models/price.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { PriceDTO } from '@generated/swagger/inventory-api';
|
||||||
|
import { PriceValue } from './price-value';
|
||||||
|
|
||||||
|
export interface Price extends PriceDTO {
|
||||||
|
value: PriceValue;
|
||||||
|
}
|
||||||
9
libs/remission/data-access/src/lib/models/product.ts
Normal file
9
libs/remission/data-access/src/lib/models/product.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { ProductDTO } from '@generated/swagger/inventory-api';
|
||||||
|
|
||||||
|
export interface Product extends ProductDTO {
|
||||||
|
name: string;
|
||||||
|
contributors: string;
|
||||||
|
ean: string;
|
||||||
|
format: string;
|
||||||
|
formatDetail: string;
|
||||||
|
}
|
||||||
8
libs/remission/data-access/src/lib/models/return-item.ts
Normal file
8
libs/remission/data-access/src/lib/models/return-item.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { ReturnItemDTO } from '@generated/swagger/inventory-api';
|
||||||
|
import { Product } from './product';
|
||||||
|
import { Price } from './price';
|
||||||
|
|
||||||
|
export interface ReturnItem extends ReturnItemDTO {
|
||||||
|
product: Product;
|
||||||
|
retailPrice: Price;
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
export const greeting = 'Hello World!';
|
|
||||||
2
libs/remission/shared/product/src/index.ts
Normal file
2
libs/remission/shared/product/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './lib/product-info/product-info.component';
|
||||||
|
export * from './lib/product-stock-info/product-stock-info.component';
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
@let product = item().product;
|
||||||
|
@let price = item().retailPrice;
|
||||||
|
@let horizontal = orientation() === 'horizontal';
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
class="w-full h-auto object-contain"
|
||||||
|
sharedProductRouterLink
|
||||||
|
sharedProductImage
|
||||||
|
[ean]="product.ean"
|
||||||
|
[alt]="product.name"
|
||||||
|
data-what="product-image"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="grid"
|
||||||
|
[class.grid-cols-[minmax(20rem,1fr),auto]]="horizontal"
|
||||||
|
[class.gap-6]="horizontal"
|
||||||
|
[class.grid-flow-row]="!horizontal"
|
||||||
|
[class.gap-2]="!horizontal"
|
||||||
|
>
|
||||||
|
<div class="grid grid-flow-row gap-2">
|
||||||
|
<div class="isa-text-body-2-bold" data-what="product-contributors">
|
||||||
|
{{ product.contributors }}
|
||||||
|
</div>
|
||||||
|
<div class="isa-text-subtitle-1-regular" data-what="product-name">
|
||||||
|
{{ product.name }}
|
||||||
|
</div>
|
||||||
|
<div class="isa-text-body-2-bold" data-what="product-price">
|
||||||
|
{{ price.value.value | currency: price.value.currencySymbol }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col w-full gap-2 items-start justify-end">
|
||||||
|
<shared-product-format
|
||||||
|
[format]="product.format"
|
||||||
|
[formatDetail]="product.formatDetail"
|
||||||
|
data-what="product-format"
|
||||||
|
></shared-product-format>
|
||||||
|
<div
|
||||||
|
class="isa-text-body-2-regular text-isa-neutral-600"
|
||||||
|
data-what="product-ean"
|
||||||
|
>
|
||||||
|
{{ product.ean }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
|
||||||
|
import {
|
||||||
|
ProductInfoComponent,
|
||||||
|
ProductInfoItem,
|
||||||
|
} from './product-info.component';
|
||||||
|
import { ProductImageDirective } from '@isa/shared/product-image';
|
||||||
|
import { ProductRouterLinkDirective } from '@isa/shared/product-router-link';
|
||||||
|
import { MockDirectives } from 'ng-mocks';
|
||||||
|
|
||||||
|
describe('ProductInfoComponent', () => {
|
||||||
|
let spectator: Spectator<ProductInfoComponent>;
|
||||||
|
const createComponent = createComponentFactory({
|
||||||
|
component: ProductInfoComponent,
|
||||||
|
overrideComponents: [
|
||||||
|
[
|
||||||
|
ProductInfoComponent,
|
||||||
|
{
|
||||||
|
remove: {
|
||||||
|
imports: [ProductImageDirective, ProductRouterLinkDirective],
|
||||||
|
},
|
||||||
|
add: {
|
||||||
|
imports: MockDirectives(
|
||||||
|
ProductImageDirective,
|
||||||
|
ProductRouterLinkDirective,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockItem: ProductInfoItem = {
|
||||||
|
product: {
|
||||||
|
ean: '1234567890123',
|
||||||
|
name: 'Test Product',
|
||||||
|
contributors: '',
|
||||||
|
format: '',
|
||||||
|
formatDetail: '',
|
||||||
|
},
|
||||||
|
retailPrice: {
|
||||||
|
value: { value: 19.99, currency: 'EUR' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spectator = createComponent({ props: { item: mockItem } });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(spectator.component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set data-what and data-which attributes', () => {
|
||||||
|
const host = spectator.element;
|
||||||
|
expect(host).toHaveAttribute('data-what', 'product-info');
|
||||||
|
expect(host).toHaveAttribute('data-which', 'remission-product');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set data-ean attribute from item.product.ean', () => {
|
||||||
|
const host = spectator.element;
|
||||||
|
expect(host).toHaveAttribute('data-ean', mockItem.product.ean);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add more tests for template rendering if template is available
|
||||||
|
});
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { CurrencyPipe } from '@angular/common';
|
||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { ReturnItem } from '@isa/remission/data-access';
|
||||||
|
import { ProductImageDirective } from '@isa/shared/product-image';
|
||||||
|
import { ProductRouterLinkDirective } from '@isa/shared/product-router-link';
|
||||||
|
import { ProductFormatComponent } from '@isa/shared/product-foramt';
|
||||||
|
|
||||||
|
export type ProductInfoItem = Pick<ReturnItem, 'product' | 'retailPrice'>;
|
||||||
|
|
||||||
|
export type ProductInfoOrientation = 'horizontal' | 'vertical';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'remi-product-info',
|
||||||
|
templateUrl: 'product-info.component.html',
|
||||||
|
imports: [
|
||||||
|
ProductImageDirective,
|
||||||
|
ProductRouterLinkDirective,
|
||||||
|
CurrencyPipe,
|
||||||
|
ProductFormatComponent,
|
||||||
|
],
|
||||||
|
host: {
|
||||||
|
'[class]': 'classList',
|
||||||
|
'data-what': 'product-info',
|
||||||
|
'data-which': 'remission-product',
|
||||||
|
'[attr.data-ean]': 'item().product.ean',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class ProductInfoComponent {
|
||||||
|
readonly classList: ReadonlyArray<string> = [
|
||||||
|
'grid',
|
||||||
|
'grid-cols-[3.5rem,1fr]',
|
||||||
|
'gap-6',
|
||||||
|
'text-isa-neutral-900',
|
||||||
|
];
|
||||||
|
|
||||||
|
item = input.required<ProductInfoItem>();
|
||||||
|
|
||||||
|
orientation = input<ProductInfoOrientation>('horizontal');
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<div
|
||||||
|
class="product-stock-info-row"
|
||||||
|
data-what="stock-info-row"
|
||||||
|
data-which="current-stock"
|
||||||
|
>
|
||||||
|
<div data-what="stock-label" data-which="current-stock">
|
||||||
|
Aktueller Bestand
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="isa-text-body-2-bold"
|
||||||
|
data-what="stock-value"
|
||||||
|
data-which="current-stock"
|
||||||
|
>
|
||||||
|
{{ stock() }}x
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="product-stock-info-row"
|
||||||
|
data-what="stock-info-row"
|
||||||
|
data-which="remit-amount"
|
||||||
|
>
|
||||||
|
<div data-what="stock-label" data-which="remit-amount">Remi Menge</div>
|
||||||
|
<div
|
||||||
|
class="isa-text-body-2-bold"
|
||||||
|
data-what="stock-value"
|
||||||
|
data-which="remit-amount"
|
||||||
|
>
|
||||||
|
{{ stockToRemit() }}x
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="product-stock-info-row"
|
||||||
|
data-what="stock-info-row"
|
||||||
|
data-which="remaining-stock"
|
||||||
|
>
|
||||||
|
<div data-what="stock-label" data-which="remaining-stock">
|
||||||
|
Übriger Bestand
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="isa-text-body-2-bold"
|
||||||
|
data-what="stock-value"
|
||||||
|
data-which="remaining-stock"
|
||||||
|
>
|
||||||
|
{{ targetStock() }}x
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="product-stock-info-row" data-what="stock-info-row" data-which="zob">
|
||||||
|
<div data-what="stock-label" data-which="zob">ZOB</div>
|
||||||
|
<div
|
||||||
|
class="isa-text-body-2-bold grid-flow-row"
|
||||||
|
data-what="stock-value"
|
||||||
|
data-which="zob"
|
||||||
|
>
|
||||||
|
{{ zob() }}x
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
:host {
|
||||||
|
@apply grid grid-flow-row gap-2 text-isa-neutral-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-stock-info-row {
|
||||||
|
@apply flex items-center gap-2 justify-between;
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
|
||||||
|
import { ProductStockInfoComponent } from './product-stock-info.component';
|
||||||
|
|
||||||
|
describe('ProductStockInfoComponent', () => {
|
||||||
|
let spectator: Spectator<ProductStockInfoComponent>;
|
||||||
|
const createComponent = createComponentFactory(ProductStockInfoComponent);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spectator = createComponent();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(spectator.component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the current stock', () => {
|
||||||
|
spectator.setInput('stock', 42);
|
||||||
|
spectator.detectChanges();
|
||||||
|
const value = spectator.query(
|
||||||
|
'[data-what="stock-value"][data-which="current-stock"]',
|
||||||
|
);
|
||||||
|
expect(value).toHaveText('42x');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the remit amount', () => {
|
||||||
|
spectator.setInput('stockToRemit', 7);
|
||||||
|
spectator.detectChanges();
|
||||||
|
const value = spectator.query(
|
||||||
|
'[data-what="stock-value"][data-which="remit-amount"]',
|
||||||
|
);
|
||||||
|
expect(value).toHaveText('7x');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the remaining stock (targetStock)', () => {
|
||||||
|
spectator.setInput('stock', 20);
|
||||||
|
spectator.setInput('stockToRemit', 5);
|
||||||
|
spectator.detectChanges();
|
||||||
|
const value = spectator.query(
|
||||||
|
'[data-what="stock-value"][data-which="remaining-stock"]',
|
||||||
|
);
|
||||||
|
expect(value).toHaveText('15x');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the zob value', () => {
|
||||||
|
spectator.component.zob.set(99);
|
||||||
|
spectator.detectChanges();
|
||||||
|
const value = spectator.query(
|
||||||
|
'[data-what="stock-value"][data-which="zob"]',
|
||||||
|
);
|
||||||
|
expect(value).toHaveText('99x');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render all labels with correct e2e attributes', () => {
|
||||||
|
const labels = [
|
||||||
|
{ which: 'current-stock', text: 'Aktueller Bestand' },
|
||||||
|
{ which: 'remit-amount', text: 'Remi Menge' },
|
||||||
|
{ which: 'remaining-stock', text: 'Übriger Bestand' },
|
||||||
|
{ which: 'zob', text: 'ZOB' },
|
||||||
|
];
|
||||||
|
labels.forEach(({ which, text }) => {
|
||||||
|
const label = spectator.query(
|
||||||
|
`[data-what="stock-label"][data-which="${which}"]`,
|
||||||
|
);
|
||||||
|
expect(label).toHaveText(text);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
computed,
|
||||||
|
input,
|
||||||
|
signal,
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'remi-product-stock-info',
|
||||||
|
templateUrl: './product-stock-info.component.html',
|
||||||
|
styleUrls: ['./product-stock-info.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class ProductStockInfoComponent {
|
||||||
|
stock = input<number>(0);
|
||||||
|
|
||||||
|
stockToRemit = input<number>(0);
|
||||||
|
|
||||||
|
targetStock = computed(() => {
|
||||||
|
const stock = this.stock();
|
||||||
|
const stockToRemit = this.stockToRemit();
|
||||||
|
return stock - stockToRemit;
|
||||||
|
});
|
||||||
|
|
||||||
|
zob = signal(0);
|
||||||
|
}
|
||||||
7
libs/shared/product-format/README.md
Normal file
7
libs/shared/product-format/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# shared-product-format
|
||||||
|
|
||||||
|
This library was generated with [Nx](https://nx.dev).
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `nx test shared-product-format` to execute the unit tests.
|
||||||
34
libs/shared/product-format/eslint.config.js
Normal file
34
libs/shared/product-format/eslint.config.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
const nx = require('@nx/eslint-plugin');
|
||||||
|
const baseConfig = require('../../../eslint.config.js');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
...baseConfig,
|
||||||
|
...nx.configs['flat/angular'],
|
||||||
|
...nx.configs['flat/angular-template'],
|
||||||
|
{
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
rules: {
|
||||||
|
'@angular-eslint/directive-selector': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
type: 'attribute',
|
||||||
|
prefix: 'shared',
|
||||||
|
style: 'camelCase',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@angular-eslint/component-selector': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
prefix: 'shared',
|
||||||
|
style: 'kebab-case',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.html'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
21
libs/shared/product-format/jest.config.ts
Normal file
21
libs/shared/product-format/jest.config.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
export default {
|
||||||
|
displayName: 'shared-product-format',
|
||||||
|
preset: '../../../jest.preset.js',
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||||
|
coverageDirectory: '../../../coverage/libs/shared/product-format',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.(ts|mjs|js|html)$': [
|
||||||
|
'jest-preset-angular',
|
||||||
|
{
|
||||||
|
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
||||||
|
snapshotSerializers: [
|
||||||
|
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||||
|
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||||
|
'jest-preset-angular/build/serializers/html-comment',
|
||||||
|
],
|
||||||
|
};
|
||||||
20
libs/shared/product-format/project.json
Normal file
20
libs/shared/product-format/project.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "shared-product-format",
|
||||||
|
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "libs/shared/product-format/src",
|
||||||
|
"prefix": "shared",
|
||||||
|
"projectType": "library",
|
||||||
|
"tags": [],
|
||||||
|
"targets": {
|
||||||
|
"test": {
|
||||||
|
"executor": "@nx/jest:jest",
|
||||||
|
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "libs/shared/product-format/jest.config.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nx/eslint:lint"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
libs/shared/product-format/src/index.ts
Normal file
2
libs/shared/product-format/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './lib/product-format-icon.component';
|
||||||
|
export * from './lib/product-format.component';
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
|
||||||
|
import { ProductFormatIconComponent } from './product-format-icon.component';
|
||||||
|
import { NgIcon } from '@ng-icons/core';
|
||||||
|
|
||||||
|
describe('ProductFormatIconComponent', () => {
|
||||||
|
let spectator: Spectator<ProductFormatIconComponent>;
|
||||||
|
const createComponent = createComponentFactory({
|
||||||
|
component: ProductFormatIconComponent,
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spectator = createComponent({ props: { format: 'HC' } });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(spectator.component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render ng-icon with lowercased format as name', () => {
|
||||||
|
const icon = spectator.query(NgIcon);
|
||||||
|
expect(icon).toBeTruthy();
|
||||||
|
expect(icon?.name()).toBe('hc');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set correct data attributes on host', () => {
|
||||||
|
const host = spectator.element;
|
||||||
|
expect(host).toHaveAttribute('data-what', 'product-format-icon');
|
||||||
|
expect(host).toHaveAttribute('data-which', 'shared-product-format-icon');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Component, computed, input } from '@angular/core';
|
||||||
|
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||||
|
import { ProductFormatIconGroup } from '@isa/icons';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'shared-product-format-icon',
|
||||||
|
template: `<ng-icon [name]="formatLowerCase()" size="1.5rem"></ng-icon>`,
|
||||||
|
imports: [NgIcon],
|
||||||
|
providers: [provideIcons(ProductFormatIconGroup)],
|
||||||
|
host: {
|
||||||
|
'[class]': '["inline-block","text-isa-neutral-900", "size-6"]',
|
||||||
|
'data-what': 'product-format-icon',
|
||||||
|
'data-which': 'shared-product-format-icon',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class ProductFormatIconComponent {
|
||||||
|
format = input.required<string>();
|
||||||
|
|
||||||
|
formatLowerCase = computed(() => this.format().toLowerCase());
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
|
||||||
|
import { ProductFormatComponent } from './product-format.component';
|
||||||
|
import { ProductFormatIconComponent } from './product-format-icon.component';
|
||||||
|
|
||||||
|
describe('ProductFormatComponent', () => {
|
||||||
|
let spectator: Spectator<ProductFormatComponent>;
|
||||||
|
const createComponent = createComponentFactory({
|
||||||
|
component: ProductFormatComponent,
|
||||||
|
imports: [ProductFormatIconComponent],
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spectator = createComponent({
|
||||||
|
props: { format: 'HC', formatDetail: 'Hardcover' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(spectator.component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render ProductFormatIconComponent with correct format', () => {
|
||||||
|
const icon = spectator.query(ProductFormatIconComponent);
|
||||||
|
expect(icon).toBeTruthy();
|
||||||
|
// Check input binding
|
||||||
|
expect(icon?.format()).toBe('HC');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the formatDetail text', () => {
|
||||||
|
expect(spectator.query('span')).toHaveText('Hardcover');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set correct data attributes on host', () => {
|
||||||
|
const host = spectator.element;
|
||||||
|
expect(host).toHaveAttribute('data-what', 'product-format');
|
||||||
|
expect(host).toHaveAttribute('data-which', 'shared-product-format');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
||||||
|
import { ProductFormatIconComponent } from './product-format-icon.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'shared-product-format',
|
||||||
|
template: `<shared-product-format-icon
|
||||||
|
[format]="format()"
|
||||||
|
></shared-product-format-icon>
|
||||||
|
<span class="isa-text-body-2-regular text-isa-secondary-900 truncate">
|
||||||
|
{{ formatDetail() }}
|
||||||
|
</span>`,
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [ProductFormatIconComponent],
|
||||||
|
host: {
|
||||||
|
'[class]': '["flex", "items-center", "gap-2"]',
|
||||||
|
'data-what': 'product-format',
|
||||||
|
'data-which': 'shared-product-format',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class ProductFormatComponent {
|
||||||
|
format = input.required<string>();
|
||||||
|
|
||||||
|
formatDetail = input.required<string>();
|
||||||
|
}
|
||||||
10
libs/shared/product-format/src/test-setup.ts
Normal file
10
libs/shared/product-format/src/test-setup.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment
|
||||||
|
globalThis.ngJest = {
|
||||||
|
testEnvironmentOptions: {
|
||||||
|
errorOnUnknownElements: true,
|
||||||
|
errorOnUnknownProperties: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
|
||||||
|
|
||||||
|
setupZoneTestEnv();
|
||||||
28
libs/shared/product-format/tsconfig.json
Normal file
28
libs/shared/product-format/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2022",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.lib.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extends": "../../../tsconfig.base.json",
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
|
"strictInjectionParameters": true,
|
||||||
|
"strictInputAccessModifiers": true,
|
||||||
|
"strictTemplates": true
|
||||||
|
}
|
||||||
|
}
|
||||||
17
libs/shared/product-format/tsconfig.lib.json
Normal file
17
libs/shared/product-format/tsconfig.lib.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../../dist/out-tsc",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/test-setup.ts",
|
||||||
|
"jest.config.ts",
|
||||||
|
"src/**/*.test.ts"
|
||||||
|
],
|
||||||
|
"include": ["src/**/*.ts"]
|
||||||
|
}
|
||||||
16
libs/shared/product-format/tsconfig.spec.json
Normal file
16
libs/shared/product-format/tsconfig.spec.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es2016",
|
||||||
|
"types": ["jest", "node"]
|
||||||
|
},
|
||||||
|
"files": ["src/test-setup.ts"],
|
||||||
|
"include": [
|
||||||
|
"jest.config.ts",
|
||||||
|
"src/**/*.test.ts",
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
7
libs/ui/skeleton-loader/README.md
Normal file
7
libs/ui/skeleton-loader/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# ui-skeleton-loader
|
||||||
|
|
||||||
|
This library was generated with [Nx](https://nx.dev).
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `nx test ui-skeleton-loader` to execute the unit tests.
|
||||||
34
libs/ui/skeleton-loader/eslint.config.js
Normal file
34
libs/ui/skeleton-loader/eslint.config.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
const nx = require('@nx/eslint-plugin');
|
||||||
|
const baseConfig = require('../../../eslint.config.js');
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
...baseConfig,
|
||||||
|
...nx.configs['flat/angular'],
|
||||||
|
...nx.configs['flat/angular-template'],
|
||||||
|
{
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
rules: {
|
||||||
|
'@angular-eslint/directive-selector': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
type: 'attribute',
|
||||||
|
prefix: 'ui',
|
||||||
|
style: 'camelCase',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@angular-eslint/component-selector': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
type: 'element',
|
||||||
|
prefix: 'ui',
|
||||||
|
style: 'kebab-case',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.html'],
|
||||||
|
// Override or add rules here
|
||||||
|
rules: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
21
libs/ui/skeleton-loader/jest.config.ts
Normal file
21
libs/ui/skeleton-loader/jest.config.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
export default {
|
||||||
|
displayName: 'ui-skeleton-loader',
|
||||||
|
preset: '../../../jest.preset.js',
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||||
|
coverageDirectory: '../../../coverage/libs/ui/skeleton-loader',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.(ts|mjs|js|html)$': [
|
||||||
|
'jest-preset-angular',
|
||||||
|
{
|
||||||
|
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
||||||
|
snapshotSerializers: [
|
||||||
|
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||||
|
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||||
|
'jest-preset-angular/build/serializers/html-comment',
|
||||||
|
],
|
||||||
|
};
|
||||||
20
libs/ui/skeleton-loader/project.json
Normal file
20
libs/ui/skeleton-loader/project.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "ui-skeleton-loader",
|
||||||
|
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "libs/ui/skeleton-loader/src",
|
||||||
|
"prefix": "ui",
|
||||||
|
"projectType": "library",
|
||||||
|
"tags": [],
|
||||||
|
"targets": {
|
||||||
|
"test": {
|
||||||
|
"executor": "@nx/jest:jest",
|
||||||
|
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "libs/ui/skeleton-loader/jest.config.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"executor": "@nx/eslint:lint"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
libs/ui/skeleton-loader/src/index.ts
Normal file
2
libs/ui/skeleton-loader/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './lib/skeleton-loader.directive';
|
||||||
|
export * from './lib/skeleton-loader.component';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<div class="ui-skeleton-loader-bar"></div>
|
||||||
11
libs/ui/skeleton-loader/src/lib/skeleton-loader.component.ts
Normal file
11
libs/ui/skeleton-loader/src/lib/skeleton-loader.component.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ui-skeleton-loader',
|
||||||
|
templateUrl: 'skeleton-loader.component.html',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
host: {
|
||||||
|
class: 'ui-skeleton-loader',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class SkeletonLoaderComponent {}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { Spectator, createComponentFactory } from '@ngneat/spectator/jest';
|
||||||
|
import { SkeletonLoaderDirective } from './skeleton-loader.directive';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<div
|
||||||
|
*uiSkeletonLoader="isLoading(); width: width(); height: height()"
|
||||||
|
data-testid="content"
|
||||||
|
>
|
||||||
|
Original Content
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
imports: [SkeletonLoaderDirective],
|
||||||
|
})
|
||||||
|
class TestHostComponent {
|
||||||
|
isLoading = input(false);
|
||||||
|
width = input<string | undefined>(undefined);
|
||||||
|
height = input<string | undefined>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for the SkeletonLoaderDirective.
|
||||||
|
* Tests conditional rendering of skeleton loader vs original content
|
||||||
|
* and verifies custom dimensions are applied correctly.
|
||||||
|
*/
|
||||||
|
describe('SkeletonLoaderDirective', () => {
|
||||||
|
let spectator: Spectator<TestHostComponent>;
|
||||||
|
const createTestHost = createComponentFactory(TestHostComponent);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spectator = createTestHost();
|
||||||
|
spectator.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the Original Content when isLoading is false', () => {
|
||||||
|
expect(spectator.query('[data-testid="content"]')).toHaveText(
|
||||||
|
'Original Content',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the skeleton loader when isLoading is true', () => {
|
||||||
|
spectator.setInput('isLoading', true);
|
||||||
|
expect(spectator.query('ui-skeleton-loader')).toExist();
|
||||||
|
expect(spectator.query('[data-testid="content"]')).not.toExist();
|
||||||
|
});
|
||||||
|
});
|
||||||
81
libs/ui/skeleton-loader/src/lib/skeleton-loader.directive.ts
Normal file
81
libs/ui/skeleton-loader/src/lib/skeleton-loader.directive.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import {
|
||||||
|
Directive,
|
||||||
|
ViewContainerRef,
|
||||||
|
TemplateRef,
|
||||||
|
inject,
|
||||||
|
input,
|
||||||
|
effect,
|
||||||
|
ComponentRef,
|
||||||
|
untracked,
|
||||||
|
EmbeddedViewRef,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { SkeletonLoaderComponent } from './skeleton-loader.component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structural directive that conditionally replaces its content with a skeleton loader.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <div *uiSkeletonLoader="isLoading">Content to hide while loading</div>
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <div *uiSkeletonLoader="isLoading; width: '100px'; height: '20px'">Content</div>
|
||||||
|
*/
|
||||||
|
@Directive({
|
||||||
|
selector: '[uiSkeletonLoader]',
|
||||||
|
})
|
||||||
|
export class SkeletonLoaderDirective {
|
||||||
|
#viewContainerRef = inject(ViewContainerRef);
|
||||||
|
#templateRef = inject(TemplateRef);
|
||||||
|
|
||||||
|
private componentRef: ComponentRef<SkeletonLoaderComponent> | null = null;
|
||||||
|
private embeddedViewRef: EmbeddedViewRef<unknown> | null = null;
|
||||||
|
|
||||||
|
uiSkeletonLoader = input<boolean>(false);
|
||||||
|
|
||||||
|
uiSkeletonLoaderWidth = input<string | undefined>(undefined);
|
||||||
|
|
||||||
|
uiSkeletonLoaderHeight = input<string | undefined>(undefined);
|
||||||
|
|
||||||
|
render = effect(() => {
|
||||||
|
const condition = this.uiSkeletonLoader();
|
||||||
|
|
||||||
|
if (condition && !this.componentRef) {
|
||||||
|
// Create the skeleton loader when condition is true and view isn't created
|
||||||
|
this.#viewContainerRef.clear();
|
||||||
|
this.componentRef = this.#viewContainerRef.createComponent(
|
||||||
|
SkeletonLoaderComponent,
|
||||||
|
);
|
||||||
|
|
||||||
|
untracked(() => {
|
||||||
|
this.applyStyles();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.embeddedViewRef = null; // Clear the embedded view reference
|
||||||
|
} else if (!condition && !this.embeddedViewRef) {
|
||||||
|
// Show original content when condition is false
|
||||||
|
this.#viewContainerRef.clear();
|
||||||
|
this.embeddedViewRef = this.#viewContainerRef.createEmbeddedView(
|
||||||
|
this.#templateRef,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.componentRef = null; // Clear the component reference
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateStyles = effect(() => {
|
||||||
|
this.applyStyles();
|
||||||
|
});
|
||||||
|
|
||||||
|
applyStyles() {
|
||||||
|
if (this.componentRef) {
|
||||||
|
const width = this.uiSkeletonLoaderWidth() || '100%';
|
||||||
|
const height = this.uiSkeletonLoaderHeight() || '100%';
|
||||||
|
const element: HTMLElement | undefined =
|
||||||
|
this.componentRef?.location?.nativeElement;
|
||||||
|
if (element) {
|
||||||
|
element.style.width = width;
|
||||||
|
element.style.height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
libs/ui/skeleton-loader/src/skeleton-loader.scss
Normal file
25
libs/ui/skeleton-loader/src/skeleton-loader.scss
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
.ui-skeleton-loader {
|
||||||
|
@apply inline-block min-h-[1rem] min-w-[2rem] overflow-hidden rounded bg-isa-neutral-300 relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-skeleton-loader-bar {
|
||||||
|
@keyframes loader {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@apply block absolute top-0 left-0 right-0 bottom-0;
|
||||||
|
|
||||||
|
@apply bg-gradient-to-r from-isa-neutral-300 via-isa-neutral-400 to-isa-neutral-300;
|
||||||
|
|
||||||
|
animation: loader 1s ease-in-out infinite;
|
||||||
|
}
|
||||||
10
libs/ui/skeleton-loader/src/test-setup.ts
Normal file
10
libs/ui/skeleton-loader/src/test-setup.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment
|
||||||
|
globalThis.ngJest = {
|
||||||
|
testEnvironmentOptions: {
|
||||||
|
errorOnUnknownElements: true,
|
||||||
|
errorOnUnknownProperties: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
|
||||||
|
|
||||||
|
setupZoneTestEnv();
|
||||||
28
libs/ui/skeleton-loader/tsconfig.json
Normal file
28
libs/ui/skeleton-loader/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2022",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.lib.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extends": "../../../tsconfig.base.json",
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
|
"strictInjectionParameters": true,
|
||||||
|
"strictInputAccessModifiers": true,
|
||||||
|
"strictTemplates": true
|
||||||
|
}
|
||||||
|
}
|
||||||
17
libs/ui/skeleton-loader/tsconfig.lib.json
Normal file
17
libs/ui/skeleton-loader/tsconfig.lib.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../../dist/out-tsc",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/test-setup.ts",
|
||||||
|
"jest.config.ts",
|
||||||
|
"src/**/*.test.ts"
|
||||||
|
],
|
||||||
|
"include": ["src/**/*.ts"]
|
||||||
|
}
|
||||||
16
libs/ui/skeleton-loader/tsconfig.spec.json
Normal file
16
libs/ui/skeleton-loader/tsconfig.spec.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es2016",
|
||||||
|
"types": ["jest", "node"]
|
||||||
|
},
|
||||||
|
"files": ["src/test-setup.ts"],
|
||||||
|
"include": [
|
||||||
|
"jest.config.ts",
|
||||||
|
"src/**/*.test.ts",
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
5533
package-lock.json
generated
5533
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -78,11 +78,8 @@
|
|||||||
"@schematics/angular": "20.0.2",
|
"@schematics/angular": "20.0.2",
|
||||||
"@softarc/eslint-plugin-sheriff": "^0.18.0",
|
"@softarc/eslint-plugin-sheriff": "^0.18.0",
|
||||||
"@softarc/sheriff-core": "0.18.0",
|
"@softarc/sheriff-core": "0.18.0",
|
||||||
"@storybook/angular": "9.0.9",
|
"@storybook/addon-docs": "^9.0.11",
|
||||||
"@storybook/core-server": "8.6.14",
|
"@storybook/angular": "^9.0.5",
|
||||||
"@storybook/jest": "0.2.3",
|
|
||||||
"@storybook/test-runner": "0.23.0",
|
|
||||||
"@storybook/testing-library": "0.2.2",
|
|
||||||
"@swc-node/register": "1.10.10",
|
"@swc-node/register": "1.10.10",
|
||||||
"@swc/core": "1.12.1",
|
"@swc/core": "1.12.1",
|
||||||
"@swc/helpers": "0.5.17",
|
"@swc/helpers": "0.5.17",
|
||||||
@@ -104,13 +101,14 @@
|
|||||||
"jest-preset-angular": "14.6.0",
|
"jest-preset-angular": "14.6.0",
|
||||||
"jsonc-eslint-parser": "^2.1.0",
|
"jsonc-eslint-parser": "^2.1.0",
|
||||||
"ng-mocks": "14.13.5",
|
"ng-mocks": "14.13.5",
|
||||||
"ng-packagr": "~18.2.0",
|
"ng-packagr": "^20.0.0",
|
||||||
"ng-swagger-gen": "^2.3.1",
|
"ng-swagger-gen": "^2.3.1",
|
||||||
"nx": "21.2.0",
|
"nx": "21.2.0",
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
"postcss-url": "~10.1.3",
|
"postcss-url": "~10.1.3",
|
||||||
"prettier": "^3.5.2",
|
"prettier": "^3.5.2",
|
||||||
"pretty-quick": "~4.0.0",
|
"pretty-quick": "~4.0.0",
|
||||||
|
"storybook": "^9.0.5",
|
||||||
"tailwindcss": "^3.4.14",
|
"tailwindcss": "^3.4.14",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
"ts-node": "10.9.1",
|
"ts-node": "10.9.1",
|
||||||
|
|||||||
@@ -75,10 +75,14 @@
|
|||||||
],
|
],
|
||||||
"@isa/remission/helpers": ["libs/remission/helpers/src/index.ts"],
|
"@isa/remission/helpers": ["libs/remission/helpers/src/index.ts"],
|
||||||
"@isa/remission/shared": ["libs/remission/shared/src/index.ts"],
|
"@isa/remission/shared": ["libs/remission/shared/src/index.ts"],
|
||||||
"@isa/remission/shared/product-info": [
|
"@isa/remission/shared/product-details": [
|
||||||
"libs/remission/shared/product-info/src/index.ts"
|
"libs/remission/shared/product-details/src/index.ts"
|
||||||
|
],
|
||||||
|
"@isa/remission/shared/product": [
|
||||||
|
"libs/remission/shared/product/src/index.ts"
|
||||||
],
|
],
|
||||||
"@isa/shared/filter": ["libs/shared/filter/src/index.ts"],
|
"@isa/shared/filter": ["libs/shared/filter/src/index.ts"],
|
||||||
|
"@isa/shared/product-foramt": ["libs/shared/product-format/src/index.ts"],
|
||||||
"@isa/shared/product-image": ["libs/shared/product-image/src/index.ts"],
|
"@isa/shared/product-image": ["libs/shared/product-image/src/index.ts"],
|
||||||
"@isa/shared/product-router-link": [
|
"@isa/shared/product-router-link": [
|
||||||
"libs/shared/product-router-link/src/index.ts"
|
"libs/shared/product-router-link/src/index.ts"
|
||||||
@@ -95,6 +99,7 @@
|
|||||||
"@isa/ui/menu": ["libs/ui/menu/src/index.ts"],
|
"@isa/ui/menu": ["libs/ui/menu/src/index.ts"],
|
||||||
"@isa/ui/progress-bar": ["libs/ui/progress-bar/src/index.ts"],
|
"@isa/ui/progress-bar": ["libs/ui/progress-bar/src/index.ts"],
|
||||||
"@isa/ui/search-bar": ["libs/ui/search-bar/src/index.ts"],
|
"@isa/ui/search-bar": ["libs/ui/search-bar/src/index.ts"],
|
||||||
|
"@isa/ui/skeleton-loader": ["libs/ui/skeleton-loader/src/index.ts"],
|
||||||
"@isa/ui/toolbar": ["libs/ui/toolbar/src/index.ts"],
|
"@isa/ui/toolbar": ["libs/ui/toolbar/src/index.ts"],
|
||||||
"@isa/ui/tooltip": ["libs/ui/tooltip/src/index.ts"],
|
"@isa/ui/tooltip": ["libs/ui/tooltip/src/index.ts"],
|
||||||
"@isa/utils/scroll-position": ["libs/utils/scroll-position/src/index.ts"],
|
"@isa/utils/scroll-position": ["libs/utils/scroll-position/src/index.ts"],
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
"extends": "./tsconfig.base.json",
|
"extends": "./tsconfig.base.json",
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "commonjs"
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user