Skip to content
Open
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
39 changes: 39 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,45 @@
}
}
}
},
"cross-sell-products": {
"root": "libs/cross-sell-products",
"sourceRoot": "libs/cross-sell-products",
"projectType": "library",
"prefix": "@daffodil",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "libs/cross-sell-products/tsconfig.lib.json",
"project": "libs/cross-sell-products/ng-package.json"
},
"configurations": {
"production": {
"project": "libs/cross-sell-products/ng-package.prod.json",
"tsConfig": "libs/cross-sell-products/tsconfig.lib.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/cross-sell-products/test.ts",
"codeCoverage": true,
"tsConfig": "libs/cross-sell-products/tsconfig.spec.json",
"karmaConfig": "libs/cross-sell-products/karma.conf.js"
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"libs/cross-sell-products/**/*.ts",
"libs/cross-sell-products/**/*.html"
]
}
}
}
},
"product-configurable": {
"root": "libs/product-configurable",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const {
* Provider function for {@link DAFF_CART_IN_MEMORY_EXTRA_ATTRIBUTES_HOOK}.
*/
provider: provideDaffCartInMemoryExtraAttributesHook,
factoryProvider: provideDaffCartInMemoryExtraAttributesHookFactory,
} = createSingleInjectionToken<DaffCartInMemoryExtraAttributesHook>(
'DAFF_CART_IN_MEMORY_EXTRA_ATTRIBUTES_HOOK',
{ factory: () => (reqInfo, cart) => ({}) },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export {
DAFF_CART_IN_MEMORY_EXTRA_ATTRIBUTES_HOOK,
DaffCartInMemoryExtraAttributesHook,
provideDaffCartInMemoryExtraAttributesHook,
provideDaffCartInMemoryExtraAttributesHookFactory,
} from './extra-attributes-hook.token';
16 changes: 16 additions & 0 deletions libs/cross-sell-products/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# @daffodil/cross-sell-products
`@daffodil/cross-sell-products` is an optional package that adds the cross-sell products feature on to `@daffodil/product`.

## Installation
To install `@daffodil/cross-sell-products`, use the following commands in your terminal.

Install with npm:
```bash
npm install @daffodil/cross-sell-products --save
```

Install with yarn:

```bash
yarn add @daffodil/cross-sell-products
```
6 changes: 6 additions & 0 deletions libs/cross-sell-products/driver/in-memory/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "../../../../node_modules/ng-packagr/ng-entrypoint.schema.json",
"lib": {
"entryFile": "src/index.ts"
}
}
1 change: 1 addition & 0 deletions libs/cross-sell-products/driver/in-memory/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './public_api';
33 changes: 33 additions & 0 deletions libs/cross-sell-products/driver/in-memory/src/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
inject,
makeEnvironmentProviders,
} from '@angular/core';
import { RequestInfo } from 'angular-in-memory-web-api';

import { DaffCart } from '@daffodil/cart';
import { provideDaffCartInMemoryExtraAttributesHookFactory } from '@daffodil/cart/driver/in-memory';
import { DaffCartWithCrossSellProducts } from '@daffodil/cross-sell-products';
import { DaffInMemoryBackendProductService } from '@daffodil/product/driver/in-memory';

/**
* Main provider entrypoint for `@daffodil/cross-sell-products/driver/in-memory`.
*/
export const provideInMemoryCrossSellProductsDriver = () => makeEnvironmentProviders([
provideDaffCartInMemoryExtraAttributesHookFactory(() => {
const productBackend = inject(DaffInMemoryBackendProductService);
return (reqInfo: RequestInfo, cart: DaffCart): DaffCartWithCrossSellProducts => {
// look up products in the backend via cart items' product ID
// this is dependent on cart add item actually verifying that the products
// exist in the backend before adding to the cart
const crossSells = cart.items
?.map((item) => productBackend.products.find((product) => product.id === item.product_id))
.filter((e) => !!e)
?? [];
return {
...cart,
crossSells,
crossSellIds: crossSells.map((product) => product.id),
};
};
}),
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { provideInMemoryCrossSellProductsDriver } from './provider';
6 changes: 6 additions & 0 deletions libs/cross-sell-products/driver/magento/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "../../../../node_modules/ng-packagr/ng-entrypoint.schema.json",
"lib": {
"entryFile": "src/index.ts"
}
}
1 change: 1 addition & 0 deletions libs/cross-sell-products/driver/magento/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './public_api';
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { MagentoCartItem } from '@daffodil/cart/driver/magento';
import { MagentoProductPreview } from '@daffodil/product/driver/magento';

/**
* A cart whose items include a list of cross-sell products.
*/
export interface MagentoCartItemWithCrossSell extends MagentoCartItem {
product: MagentoCartItem['product'] & {
crosssell_products: Array<MagentoProductPreview>;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { MagentoCart } from '@daffodil/cart/driver/magento';

import { MagentoCartItemWithCrossSell } from './cart-item-with-cross-sell.type';

export interface MagentoCartWithCrossSell extends MagentoCart {
items: Array<MagentoCartItemWithCrossSell>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { MagentoCartItemWithCrossSell } from './cart-item-with-cross-sell.type';
export { MagentoCartWithCrossSell } from './cart-with-cross-sell.type';
45 changes: 45 additions & 0 deletions libs/cross-sell-products/driver/magento/src/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
inject,
makeEnvironmentProviders,
} from '@angular/core';

import { DaffCart } from '@daffodil/cart';
import {
provideDaffCartMagentoExtraCartFragmentFactories,
provideDaffCartMagentoCartTransformFactories,
} from '@daffodil/cart/driver/magento';
import { DaffCartWithCrossSellProducts } from '@daffodil/cross-sell-products';
import { DaffProduct } from '@daffodil/product';
import {
DAFF_PRODUCT_MAGENTO_EXTRA_PRODUCT_FRAGMENTS,
DAFF_PRODUCT_MAGENTO_EXTRA_PRODUCT_PREVIEW_FRAGMENTS,
DaffMagentoProductsTransformer,
MAGENTO_PRODUCT_CONFIG_TOKEN,
} from '@daffodil/product/driver/magento';

import { MagentoCartWithCrossSell } from './models/public_api';
import { magentoCrossSellProductsFragment } from './queries/fragments/cross-sell-products';

/**
* Main provider entrypoint for `@daffodil/cross-sell-products/driver/magento`.
*/
export const provideMagentoCrossSellProductsDriver = () => makeEnvironmentProviders([
provideDaffCartMagentoExtraCartFragmentFactories(() =>
magentoCrossSellProductsFragment([
...inject(DAFF_PRODUCT_MAGENTO_EXTRA_PRODUCT_PREVIEW_FRAGMENTS),
...inject(DAFF_PRODUCT_MAGENTO_EXTRA_PRODUCT_FRAGMENTS),
]),
),
provideDaffCartMagentoCartTransformFactories(() => {
const transformer = inject(DaffMagentoProductsTransformer);
const config = inject(MAGENTO_PRODUCT_CONFIG_TOKEN);
return (daffCart: DaffCart, magentoCart: MagentoCartWithCrossSell): DaffCartWithCrossSellProducts => {
const crossSells = magentoCart.items.reduce((acc, item) => acc.concat(item.product.crosssell_products.map((p) => transformer.transformMagentoProduct(p, config.baseMediaUrl))), <Array<DaffProduct>>[]);
return {
...daffCart,
crossSells,
crossSellIds: crossSells.map((p) => p.id),
};
};
}),
]);
4 changes: 4 additions & 0 deletions libs/cross-sell-products/driver/magento/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './models/public_api';

export { magentoCrossSellProductsFragment } from './queries/fragments/cross-sell-products';
export { provideMagentoCrossSellProductsDriver } from './provider';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { gql } from 'apollo-angular';
import { DocumentNode } from 'graphql';

import {
daffBuildFragmentDefinition,
daffBuildFragmentNameSpread,
} from '@daffodil/core/graphql';
import { magentoProductPreviewFragment } from '@daffodil/product/driver/magento';

export const magentoCrossSellProductsFragment = (extraProductFragments: DocumentNode[] = []) => gql`
fragment crossSellProducts on Cart {
items {
product {
crosssell_products {
...magentoProductPreview
${daffBuildFragmentNameSpread(...extraProductFragments)}
}
}
}
}
${magentoProductPreviewFragment}
${daffBuildFragmentDefinition(...extraProductFragments)}
`;
6 changes: 6 additions & 0 deletions libs/cross-sell-products/driver/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-entrypoint.schema.json",
"lib": {
"entryFile": "src/index.ts"
}
}
1 change: 1 addition & 0 deletions libs/cross-sell-products/driver/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './public_api';
1 change: 1 addition & 0 deletions libs/cross-sell-products/driver/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { TestBed } from '@angular/core/testing';
import { EffectsModule } from '@ngrx/effects';
import {
StoreModule,
Store,
} from '@ngrx/store';

import { DaffTestingCartDriverModule } from '@daffodil/cart/driver/testing';
import {
DAFF_CART_STORE_FEATURE_KEY ,
DaffResolveCartSuccess,
} from '@daffodil/cart/state';
import { DaffCartWithCrossSellProducts } from '@daffodil/cross-sell-products';
import {
DaffCrossSellProductStateModule,
DaffCrossSellProductStateRootSlice,
} from '@daffodil/cross-sell-products/state';
import { DaffCartWithCrossSellProductsFactory } from '@daffodil/cross-sell-products/testing';
import { DaffProductTestingDriverModule } from '@daffodil/product/driver/testing';

describe('@daffodil/cross-sell-products/state | Deduping CrossSell Products Only from Product Reducers', () => {
let store: Store<DaffCrossSellProductStateRootSlice>;
let cart: DaffCartWithCrossSellProducts;
let crossSellProductFactory: DaffCartWithCrossSellProductsFactory;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({}),
EffectsModule.forRoot(),
DaffCrossSellProductStateModule,
DaffProductTestingDriverModule.forRoot(),
DaffTestingCartDriverModule.forRoot(),
],
});

store = TestBed.inject(Store);
crossSellProductFactory = TestBed.inject(DaffCartWithCrossSellProductsFactory);

cart = crossSellProductFactory.create();
});

describe('when the cart is loaded with cross-sell products', () => {
beforeEach(() => {
store.dispatch(new DaffResolveCartSuccess(cart));
});

xit('should not store nested cross-sell products in cart state', done => {
store.subscribe((state) => {
expect(state[DAFF_CART_STORE_FEATURE_KEY].cart.cart?.crossSells).toBeFalsy();
done();
});
});
});
});
10 changes: 10 additions & 0 deletions libs/cross-sell-products/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
baseConfiguration = require('../../tools/karma/karma.conf');

module.exports = function (config) {
baseConfiguration(config);
config.set({
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/libs/cross-sell-products'),
},
});
};
8 changes: 8 additions & 0 deletions libs/cross-sell-products/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/cross-sell-products",
"deleteDestPath": false,
"lib": {
"entryFile": "src/index.ts"
}
}
7 changes: 7 additions & 0 deletions libs/cross-sell-products/ng-package.prod.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/cross-sell-products",
"lib": {
"entryFile": "src/index.ts"
}
}
58 changes: 58 additions & 0 deletions libs/cross-sell-products/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"name": "@daffodil/cross-sell-products",
"nx": {
"targets": {
"build": {
"outputs": [
"{workspaceRoot}/dist/cross-sell-products"
]
}
}
},
"version": "0.0.0-PLACEHOLDER",
"description": "Platform-agnostic models, state management, and drivers for cross-sell products, extending @daffodil/product. Part of the Daffodil ecommerce framework.",
"repository": {
"type": "git",
"url": "https://github.com/graycoreio/daffodil"
},
"author": "Graycore LLC",
"license": "MIT",
"bugs": {
"url": "https://github.com/graycoreio/daffodil/issues"
},
"publishConfig": {
"directory": "../../dist/cross-sell-products"
},
"scripts": {
"build": "ng build cross-sell-products --configuration production",
"lint": "cd ../.. && ng lint cross-sell-products",
"lint:fix": "npm run lint -- --fix",
"test": "ng test cross-sell-products --watch=false --browsers=ChromeHeadless",
"publish": "cd ../../dist/cross-sell-products && npm publish --access=public"
},
"homepage": "https://github.com/graycoreio/daffodil",
"peerDependencies": {
"@angular/common": "0.0.0-PLACEHOLDER",
"@angular/core": "0.0.0-PLACEHOLDER",
"@daffodil/core": "0.0.0-PLACEHOLDER",
"@daffodil/cart": "0.0.0-PLACEHOLDER",
"@daffodil/product": "0.0.0-PLACEHOLDER",
"@ngrx/effects": "0.0.0-PLACEHOLDER",
"@ngrx/entity": "0.0.0-PLACEHOLDER",
"@ngrx/store": "0.0.0-PLACEHOLDER",
"rxjs": "0.0.0-PLACEHOLDER"
},
"optionalDependencies": {
"@apollo/client": "0.0.0-PLACEHOLDER",
"@daffodil/driver": "0.0.0-PLACEHOLDER",
"angular-in-memory-web-api": "0.0.0-PLACEHOLDER",
"apollo-angular": "0.0.0-PLACEHOLDER",
"@faker-js/faker": "0.0.0-PLACEHOLDER"
},
"devDependencies": {
"@daffodil/core": "0.0.0-PLACEHOLDER",
"@daffodil/cart": "0.0.0-PLACEHOLDER",
"@daffodil/driver": "0.0.0-PLACEHOLDER",
"@daffodil/product": "0.0.0-PLACEHOLDER"
}
}
1 change: 1 addition & 0 deletions libs/cross-sell-products/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './public_api';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { DaffCart } from '@daffodil/cart';
import { DaffProduct } from '@daffodil/product';

/**
* An extension of a {@link DaffCart} that includes a list of cross-sell products.
*/
export interface DaffCartWithCrossSellProducts<T extends DaffProduct = DaffProduct> extends DaffCart {
/**
* A list of cross-sell products.
*/
crossSells: Array<T>;
crossSellIds: Array<T['id']>;
}
Loading
Loading