Skip to content

Commit

Permalink
fix: update combination card
Browse files Browse the repository at this point in the history
  • Loading branch information
devisscher committed Apr 18, 2024
1 parent 302b7ee commit c88b2b6
Show file tree
Hide file tree
Showing 7 changed files with 28 additions and 300 deletions.
5 changes: 1 addition & 4 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,7 @@
"link": "Learn more"
},
"combinations": {
"info": {
"one": "{discountCountLink} is set to combine.",
"other": "{discountCountLink} are set to combine."
},
"info": "is set to combine",
"multipleEligibleDiscounts": "If an item is eligible for multiple discounts, only the largest will apply.",
"counts": {
"product": {
Expand Down
67 changes: 3 additions & 64 deletions src/components/CombinationCard/CombinationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,6 @@ export function CombinationCard({

function buildChoices({
discountClass: currentDiscountClass,
combinableDiscountCounts,
discountId: currentDiscountId,
discountDescriptor,
i18n,
}: {
discountClass: DiscountClass;
Expand All @@ -152,61 +149,20 @@ function buildChoices({
i18n: I18n;
combinableDiscountCounts?: CombinableDiscountCounts;
}): ChoiceListProps['choices'] {
const hasCounts = typeof combinableDiscountCounts !== 'undefined';

const productOption = {
label: i18n.translate('options.productLabel', I18N_SCOPE),
value: DiscountClass.Product,
renderChildren: (isSelected: boolean) =>
isSelected && hasCounts ? (
<HelpText
currentDiscountClass={currentDiscountClass}
targetDiscountClass={DiscountClass.Product}
count={getActualCombiningDiscountsCount(
combinableDiscountCounts!.productDiscountsCount,
currentDiscountClass === DiscountClass.Product,
currentDiscountId,
)}
currentDiscountId={currentDiscountId}
currentDiscountName={discountDescriptor}
/>
) : null,
renderChildren: (isSelected: boolean) => (isSelected ? <HelpText /> : null),
};
const orderOption = {
label: i18n.translate('options.orderLabel', I18N_SCOPE),
value: DiscountClass.Order,
renderChildren: (isSelected: boolean) =>
isSelected && hasCounts ? (
<HelpText
currentDiscountClass={currentDiscountClass}
targetDiscountClass={DiscountClass.Order}
count={getActualCombiningDiscountsCount(
combinableDiscountCounts!.orderDiscountsCount,
currentDiscountClass === DiscountClass.Order,
currentDiscountId,
)}
currentDiscountId={currentDiscountId}
currentDiscountName={discountDescriptor}
/>
) : null,
renderChildren: (isSelected: boolean) => (isSelected ? <HelpText /> : null),
};
const shippingOption = {
label: i18n.translate('options.shippingLabel', I18N_SCOPE),
value: DiscountClass.Shipping,
renderChildren: (isSelected: boolean) =>
isSelected && hasCounts ? (
<HelpText
currentDiscountClass={currentDiscountClass}
targetDiscountClass={DiscountClass.Shipping}
count={getActualCombiningDiscountsCount(
combinableDiscountCounts!.shippingDiscountsCount,
currentDiscountClass === DiscountClass.Shipping,
currentDiscountId,
)}
currentDiscountId={currentDiscountId}
currentDiscountName={discountDescriptor}
/>
) : null,
renderChildren: (isSelected: boolean) => (isSelected ? <HelpText /> : null),
};

switch (currentDiscountClass) {
Expand All @@ -226,23 +182,6 @@ function buildChoices({
}
}

/**
* The combines with count needs to exclude the current discount if:
* - the current discount is of the same type as the combination discount type
* - the current discount is saved
*/
function getActualCombiningDiscountsCount(
numCombinableDiscountsForClass: number,
discountClassesMatch: boolean,
currentDiscountId?: string,
): number {
if (discountClassesMatch && Boolean(currentDiscountId)) {
return numCombinableDiscountsForClass - 1;
}

return numCombinableDiscountsForClass;
}

const getSelectedChoices = (
combinableDiscountTypes: CombinableDiscountTypes,
): ChoiceListProps['selected'] => [
Expand Down
92 changes: 6 additions & 86 deletions src/components/CombinationCard/components/HelpText/HelpText.tsx
Original file line number Diff line number Diff line change
@@ -1,96 +1,16 @@
import React, {useRef} from 'react';
import {Button, Text, Link, BlockStack} from '@shopify/polaris';
import React from 'react';
import {Text, BlockStack} from '@shopify/polaris';
import {useI18n} from '@shopify/react-i18n';
import {useAppBridge} from '@shopify/app-bridge-react';
// import {Modal} from '@shopify/app-bridge/actions';
// import {Action} from '@shopify/app-bridge/actions/Modal';
// import {useAppBridge} from '@shopify/app-bridge-react';

import {DiscountClass} from '../../../../constants';

const DISCOUNT_COMBINATION_MODAL_APP_BRIDGE_URL =
'shopify://app-bridge/modal/discounts-combinations';

interface Props {
currentDiscountClass: DiscountClass;
targetDiscountClass: DiscountClass;
count: number;
currentDiscountName: string;
currentDiscountId?: string;
}

export function HelpText({
currentDiscountClass,
targetDiscountClass,
currentDiscountId,
count,
currentDiscountName,
}: Props) {
const buttonWrapperRef = useRef<HTMLSpanElement>(null);
export function HelpText({}) {
const [i18n] = useI18n();
const shopify = useAppBridge();

const targetDiscountClassLabel = targetDiscountClass.toLocaleLowerCase();
const scope = `DiscountAppComponents.CombinationCard.HelpText`;

const handleModalOpen = () => {
shopify.modal.show(DISCOUNT_COMBINATION_MODAL_APP_BRIDGE_URL);
const test = {
discountOptions: {
currentDiscountName,
currentDiscountClass,
currentDiscountId,
targetDiscountClass,
},
};
console.log('test', test);

shopify.modal.hide(DISCOUNT_COMBINATION_MODAL_APP_BRIDGE_URL);
};

return count > 0 ? (
return (
<BlockStack>
<Text as="span" tone="subdued">
{i18n.translate(
'combinations.info',
{scope},
{
count,
discountCountLink: (
<span ref={buttonWrapperRef}>
<Button onClick={handleModalOpen} variant="plain">
{i18n.translate(
`combinations.counts.${targetDiscountClassLabel}`,
{scope},
{
count,
},
)}
</Button>
</span>
),
},
)}
</Text>
<Text as="span" tone="subdued">
{i18n.translate('combinations.multipleEligibleDiscounts', {scope})}
{i18n.translate('combinations.info', {scope})}
</Text>
</BlockStack>
) : (
<>
<Text as="span" tone="subdued">
{i18n.translate('title', {
scope: `${scope}.emptyState.${targetDiscountClass.toLowerCase()}`,
})}{' '}
{i18n.translate(`warning.with${currentDiscountClass.toLowerCase()}`, {
scope: `${scope}.emptyState.${targetDiscountClass.toLowerCase()}`,
})}{' '}
<Link
url={`https://help.shopify.com/${i18n.locale}/manual/discounts/combining-discounts`}
external
>
{i18n.translate(`${scope}.emptyState.link`)}
</Link>
</Text>
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import React from 'react';
import {Button} from '@shopify/polaris';
import {mountWithApp} from 'tests/utilities';
import {Action} from '@shopify/app-bridge/actions/Modal';
import {composeGid} from '@shopify/admin-graphql-api-utilities';

import {HelpText} from '../HelpText';
import {DiscountClass} from '../../../../../constants';

jest.mock('@shopify/app-bridge-react', () => ({
...jest.requireActual('@shopify/app-bridge-react'),
Expand All @@ -24,119 +20,17 @@ jest.mock('@shopify/app-bridge/actions', () => ({
}));

describe('<HelpText />', () => {
const defaultProps = {
isHidden: false,
currentDiscountClass: DiscountClass.Shipping,
targetDiscountClass: DiscountClass.Product,
count: 0,
currentDiscountName: 'my cool discount',
currentDiscountId: composeGid('DiscountNode', '1'),
};

it('does not render when isHidden is true', () => {
const helpText = mountWithApp(<HelpText {...defaultProps} />);
const helpText = mountWithApp(<HelpText />);

expect(helpText.find(HelpText)).toBeNull();
});

describe('when count is greater than 0', () => {
const countProps = {
...defaultProps,
count: 3,
};

describe('when selected', () => {
it('renders supporting copy', () => {
const helpText = mountWithApp(
<HelpText
{...countProps}
currentDiscountClass={DiscountClass.Product}
/>,
);

expect(helpText).toContainReactText(
'3 product discounts are set to combine.If an item is eligible for multiple discounts, only the largest will apply.',
);
});
});

describe('when count is 0', () => {
const emptyStateProps = {
...defaultProps,
count: 0,
};

it.each([
[
DiscountClass.Product,
'No product discounts are set to combine. To let customers use more than one discount, set up at least one product discount that combines with product discounts. Learn more',
],
[
DiscountClass.Order,
'No product discounts are set to combine. To let customers use more than one discount, set up at least one product discount that combines with order discounts. Learn more',
],
[
DiscountClass.Shipping,
'No product discounts are set to combine. To let customers use more than one discount, set up at least one product discount that combines with shipping discounts. Learn more',
],
])(
'renders empty state content when no %s discounts are set to combine with current discount',
(combinesWith, expectedText) => {
const helpText = mountWithApp(
<HelpText {...emptyStateProps} currentDiscountClass={combinesWith} />,
);
expect(helpText).toContainReactText(expectedText);
},
);
});

describe('Combinations Modal', () => {
it('renders a modal trigger when count > 0', () => {
const helpText = mountWithApp(<HelpText {...defaultProps} count={2} />);

expect(helpText).toContainReactComponent(Button, {
children: `2 product discounts`,
onClick: expect.any(Function),
variant: 'plain',
});
});

it('does not render a modal trigger when count is 0', () => {
const helpText = mountWithApp(<HelpText {...defaultProps} count={0} />);

expect(helpText).not.toContainReactComponent(Button);
});

it('dispatches a modal data and open action when triggered', () => {
const modalMock = jest.requireMock('@shopify/app-bridge/actions').Modal
.create as jest.Mock;

const mockModalCreate = {
dispatch: jest.fn(),
subscribe: jest.fn(),
};
modalMock.mockReturnValue(mockModalCreate);

const helpText = mountWithApp(<HelpText {...defaultProps} count={2} />);

helpText.find(Button)?.trigger('onClick');

expect(mockModalCreate.dispatch).toHaveBeenCalledWith(Action.DATA, {
discountOptions: {
currentDiscountClass: defaultProps.currentDiscountClass,
targetDiscountClass: defaultProps.targetDiscountClass,
currentDiscountName: defaultProps.currentDiscountName,
currentDiscountId: defaultProps.currentDiscountId,
},
});
const helpText = mountWithApp(<HelpText />);

expect(mockModalCreate.dispatch).toHaveBeenCalledWith(Action.DATA, {
discountOptions: expect.objectContaining({
currentDiscountClass: defaultProps.currentDiscountClass,
targetDiscountClass: defaultProps.targetDiscountClass,
currentDiscountName: defaultProps.currentDiscountName,
currentDiscountId: defaultProps.currentDiscountId,
}),
});
expect(helpText).toContainReactText('is set to combine');
});
});
});
Loading

0 comments on commit c88b2b6

Please sign in to comment.