Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add DiscountApplicationStrategyCard #154

Merged
merged 10 commits into from
Sep 21, 2023
5 changes: 5 additions & 0 deletions .changeset/gentle-windows-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/discount-app-components': minor
---

Add DiscountApplicationStrategyCard, which allows the user to select the discount application strategy, maximum or first.
11 changes: 11 additions & 0 deletions locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,17 @@
"oncePerOrder": "Only apply discount once per order",
"oncePerOrderHelpText": "If not selected, the amount will be taken off each eligible item in an order.",
"oncePerOrderHelpTextWithAmount": "If not selected, {fixedAmountValue} will be taken off each eligible item in an order."
},
"DiscountApplicationStrategyCard": {
"title": "Discount Application Strategy",
"first": {
"label": "First",
mathiusj marked this conversation as resolved.
Show resolved Hide resolved
mathiusj marked this conversation as resolved.
Show resolved Hide resolved
mathiusj marked this conversation as resolved.
Show resolved Hide resolved
"helpText": "Only apply the first discount with conditions that are satisfied."
},
"maximum": {
"label": "Maximum",
mathiusj marked this conversation as resolved.
Show resolved Hide resolved
mathiusj marked this conversation as resolved.
Show resolved Hide resolved
mathiusj marked this conversation as resolved.
Show resolved Hide resolved
"helpText": "Only apply the discount that offers the maximum reduction."
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import {Box, Card, ChoiceList, Text, VerticalStack} from '@shopify/polaris';
import {useI18n} from '@shopify/react-i18n';

import {DiscountApplicationStrategy, Field} from '../../types';

export interface DiscountAppStrategyProps {
/**
* The discount application strategy.
*/
strategy: Field<DiscountApplicationStrategy>;
}

const I18N_SCOPE = {
scope: 'DiscountAppComponents.DiscountApplicationStrategyCard',
};

export function DiscountApplicationStrategyCard({
strategy,
}: DiscountAppStrategyProps) {
const [i18n] = useI18n();

const handleChange = (strategies: DiscountApplicationStrategy[]) =>
strategy.onChange(strategies[0]);

return (
<Box paddingBlockEnd="4">
<Card padding="4">
<VerticalStack gap="4">
<Text variant="headingMd" as="h2">
{i18n.translate('title', I18N_SCOPE)}
</Text>
<ChoiceList
title={i18n.translate('title', I18N_SCOPE)}
titleHidden
choices={[
{
label: i18n.translate('first.label', I18N_SCOPE),
value: i18n.translate('first.label', I18N_SCOPE),
helpText: i18n.translate('first.helpText', I18N_SCOPE),
},
{
label: i18n.translate('maximum.label', I18N_SCOPE),
value: i18n.translate('maximum.label', I18N_SCOPE),
helpText: i18n.translate('maximum.helpText', I18N_SCOPE),
},
]}
selected={[strategy.value]}
onChange={handleChange}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last thing sorry :D should the value be represented by the ENUM here? Instead of a translated string?

Copy link
Contributor Author

@mathiusj mathiusj Sep 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you're referring specifically to the selected value, then I think it would be fine so long as the pattern in MethodCard is accurately representing the pattern we should value.

However, I do see that the choices' values can and should be updated to the enum!?

screenshot for reference:
image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was referring to the value prop in the choices :) I guess my comment was at the wrong spot!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated!

/>
</VerticalStack>
</Card>
</Box>
);
}
13 changes: 13 additions & 0 deletions src/components/DiscountApplicationStrategyCard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# DiscountAppStategyCard

The Discount Application Strategy Card enables users to toggle between `discountApplicationStrategy`s to select between `FIRST` and `MAXIMUM` strategies.

---

## Examples

### Basic usage

```jsx
<DiscountAppStategyCard strategy="ALL" />
```
1 change: 1 addition & 0 deletions src/components/DiscountApplicationStrategyCard/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './DiscountApplicationStrategyCard';
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import {ChoiceList, TextField} from '@shopify/polaris';
import {mockField, mountWithApp} from 'tests/utilities';

import {DiscountApplicationStrategyCard} from '../DiscountApplicationStrategyCard';
import {DiscountApplicationStrategy} from '../../../types';

describe('<DiscountApplicationStrategyCard />', () => {
const mockProps = {
strategy: mockField(DiscountApplicationStrategy.First),
};

afterEach(() => {
jest.resetAllMocks();
});

it('renders a DiscountApplicationStrategyCard', () => {
const methodCard = mountWithApp(
<DiscountApplicationStrategyCard {...mockProps} />,
);

expect(methodCard).not.toContainReactComponent(TextField, {
label: 'Title',
});
});

it('calls onChange when the strategy is changed', () => {
const methodCard = mountWithApp(
<DiscountApplicationStrategyCard {...mockProps} />,
);

methodCard
.find(ChoiceList)
?.trigger('onChange', [DiscountApplicationStrategy.Maximum]);

expect(mockProps.strategy.onChange).toHaveBeenCalledWith(
DiscountApplicationStrategy.Maximum,
);
});
});
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,4 @@ export {onBreadcrumbAction, handleRedirect} from './utilities/navigation';

export {generateRandomDiscountCode} from './components/DiscountCodeGenerator/utilities';
export {ValueCard} from './components/ValueCard';
export {DiscountApplicationStrategyCard} from './components/DiscountApplicationStrategyCard';
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import {Provider} from '../../foundation/Provider';
import DiscountApplicationStrategyCard from './DiscountApplicationStrategyCardPattern';

// eslint-disable-next-line import/no-default-export, import/no-anonymous-default-export
export default {
title: 'DiscountApplicationStrategyCard pattern',
parameters: {
layout: 'fullscreen',
},
};

const ApplicationStrategyCardPattern = () => (
<Provider>
<DiscountApplicationStrategyCard />
</Provider>
);

export {ApplicationStrategyCardPattern};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, {useState} from 'react';

import {Page} from '@shopify/polaris';
import {DiscountApplicationStrategyCard} from '../../../components/DiscountApplicationStrategyCard';
import {DiscountApplicationStrategy} from '../../../types';

export default function MethodCardPattern() {
const [strategy, setStrategy] = useState<DiscountApplicationStrategy>(
DiscountApplicationStrategy.First,
);
return (
<Page>
<DiscountApplicationStrategyCard
strategy={{
value: strategy,
onChange: setStrategy,
}}
/>
</Page>
);
}
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,8 @@ export interface CombinableDiscountCounts {
}

export type CountryCode = SupportedCountryCode | typeof REST_OF_WORLD;

export enum DiscountApplicationStrategy {
First = 'FIRST',
Maximum = 'MAXIMUM',
}
124 changes: 124 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3037,6 +3037,28 @@
"@storybook/preview-api" "7.3.2"
"@storybook/types" "7.3.2"

"@storybook/builder-manager@7.3.2":
version "7.3.2"
resolved "https://registry.yarnpkg.com/@storybook/builder-manager/-/builder-manager-7.3.2.tgz#6a6c98143007f14e553fc9a106033fe721cfa8a4"
integrity sha512-M0zdzpnZSg6Gd/QiIbOJkVoifAADpMT85NOC5zuAg3h3o29hedVBAigv/CE2nSbuwZtqPifjxs1AUh7wgtmj8A==
dependencies:
"@fal-works/esbuild-plugin-global-externals" "^2.1.2"
"@storybook/core-common" "7.3.2"
"@storybook/manager" "7.3.2"
"@storybook/node-logger" "7.3.2"
"@types/ejs" "^3.1.1"
"@types/find-cache-dir" "^3.2.1"
"@yarnpkg/esbuild-plugin-pnp" "^3.0.0-rc.10"
browser-assert "^1.2.1"
ejs "^3.1.8"
esbuild "^0.18.0"
esbuild-plugin-alias "^0.2.1"
express "^4.17.3"
find-cache-dir "^3.0.0"
fs-extra "^11.1.0"
process "^0.11.10"
util "^0.12.4"

"@storybook/builder-manager@7.4.2":
version "7.4.2"
resolved "https://registry.yarnpkg.com/@storybook/builder-manager/-/builder-manager-7.4.2.tgz#de00614fbe14dc3d7929fdc96c495fff5d883182"
Expand Down Expand Up @@ -3248,6 +3270,26 @@
dependencies:
"@storybook/global" "^5.0.0"

"@storybook/codemod@7.3.2":
version "7.3.2"
resolved "https://registry.yarnpkg.com/@storybook/codemod/-/codemod-7.3.2.tgz#006b0495b9b9565bf3b883524d80974eaa661bae"
integrity sha512-B2P91aYhlxdk7zeQOq0VBnDox2HEcboP2unSh6Vcf4V8j2FCdPvBIM7ZkT9p15FHfyOHvvrtf56XdBIyD8/XJA==
dependencies:
"@babel/core" "^7.22.9"
"@babel/preset-env" "^7.22.9"
"@babel/types" "^7.22.5"
"@storybook/csf" "^0.1.0"
"@storybook/csf-tools" "7.3.2"
"@storybook/node-logger" "7.3.2"
"@storybook/types" "7.3.2"
"@types/cross-spawn" "^6.0.2"
cross-spawn "^7.0.3"
globby "^11.0.2"
jscodeshift "^0.14.0"
lodash "^4.17.21"
prettier "^2.8.0"
recast "^0.23.1"

"@storybook/codemod@7.4.2":
version "7.4.2"
resolved "https://registry.yarnpkg.com/@storybook/codemod/-/codemod-7.4.2.tgz#fd5da534271d28c9355f59961c9bf4ef91f0cf5e"
Expand Down Expand Up @@ -3370,6 +3412,54 @@
dependencies:
ts-dedent "^2.0.0"

"@storybook/core-server@7.3.2":
version "7.3.2"
resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-7.3.2.tgz#b5abc90c7ed3900531b79a63c8f4059e4702f7d4"
integrity sha512-TLMEptmfqYLu4bayRV5m8T3R50uR07Fwja1n/8CCmZOGWjnr5kXMFRkD7+hj7wm82yoidfd23bmVcRU9mlG+tg==
dependencies:
"@aw-web-design/x-default-browser" "1.4.126"
"@discoveryjs/json-ext" "^0.5.3"
"@storybook/builder-manager" "7.3.2"
"@storybook/channels" "7.3.2"
"@storybook/core-common" "7.3.2"
"@storybook/core-events" "7.3.2"
"@storybook/csf" "^0.1.0"
"@storybook/csf-tools" "7.3.2"
"@storybook/docs-mdx" "^0.1.0"
"@storybook/global" "^5.0.0"
"@storybook/manager" "7.3.2"
"@storybook/node-logger" "7.3.2"
"@storybook/preview-api" "7.3.2"
"@storybook/telemetry" "7.3.2"
"@storybook/types" "7.3.2"
"@types/detect-port" "^1.3.0"
"@types/node" "^16.0.0"
"@types/pretty-hrtime" "^1.0.0"
"@types/semver" "^7.3.4"
better-opn "^3.0.2"
chalk "^4.1.0"
cli-table3 "^0.6.1"
compression "^1.7.4"
detect-port "^1.3.0"
express "^4.17.3"
fs-extra "^11.1.0"
globby "^11.0.2"
ip "^2.0.0"
lodash "^4.17.21"
open "^8.4.0"
pretty-hrtime "^1.0.3"
prompts "^2.4.0"
read-pkg-up "^7.0.1"
semver "^7.3.7"
serve-favicon "^2.5.0"
telejson "^7.0.3"
tiny-invariant "^1.3.1"
ts-dedent "^2.0.0"
util "^0.12.4"
util-deprecate "^1.0.2"
watchpack "^2.2.0"
ws "^8.2.3"

"@storybook/core-server@7.4.2":
version "7.4.2"
resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-7.4.2.tgz#ce2568d9793d752a3319cc8a907ea07264ef9e2d"
Expand Down Expand Up @@ -3429,6 +3519,21 @@
"@types/node" "^16.0.0"
ts-dedent "^2.0.0"

"@storybook/csf-tools@7.3.2":
version "7.3.2"
resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-7.3.2.tgz#d93a47443ee24e59bc444f97234e11b2fb157136"
integrity sha512-54UaOsx9QZxiuMSpX01kSAEYuZYaB72Zz8ihlVrKZbIPTSJ6SYcM/jzNCGf1Rz7AjgU2UjXCSs5zBq5t37Nuqw==
dependencies:
"@babel/generator" "^7.22.9"
"@babel/parser" "^7.22.7"
"@babel/traverse" "^7.22.8"
"@babel/types" "^7.22.5"
"@storybook/csf" "^0.1.0"
"@storybook/types" "7.3.2"
fs-extra "^11.1.0"
recast "^0.23.1"
ts-dedent "^2.0.0"

"@storybook/csf-tools@7.4.2":
version "7.4.2"
resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-7.4.2.tgz#55e4c275d8ce7ef2f67d473d5a74f2a6f7964ad9"
Expand Down Expand Up @@ -3511,6 +3616,11 @@
telejson "^7.0.3"
ts-dedent "^2.0.0"

"@storybook/manager@7.3.2":
version "7.3.2"
resolved "https://registry.yarnpkg.com/@storybook/manager/-/manager-7.3.2.tgz#9554d24b220c8547a75e9cbd714eddfc2c4a5824"
integrity sha512-nA3XcnD36WUjgMCtID2M4DWYZh6MnabItXvKXGbNUkI8SVaIekc5nEgeplFyqutL11eKz3Es/FwwEP+mePbWfw==

"@storybook/manager@7.4.2":
version "7.4.2"
resolved "https://registry.yarnpkg.com/@storybook/manager/-/manager-7.4.2.tgz#423312640930b39c3263f2db9a0ffd564045cc53"
Expand Down Expand Up @@ -3697,6 +3807,20 @@
"@storybook/client-logger" "7.3.2"
"@storybook/preview-api" "7.3.2"

"@storybook/telemetry@7.3.2":
version "7.3.2"
resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-7.3.2.tgz#61c07300dedd40f3c17c81ea9d2a097187cb37c9"
integrity sha512-BmgwaZGoR2ZzGZpcO5ipc4uMd9y28qmu9Ynx054Q3mb86daJrw4CU18TVi5UoFa9qmygQhoHx2gaK2QStNtqCg==
dependencies:
"@storybook/client-logger" "7.3.2"
"@storybook/core-common" "7.3.2"
"@storybook/csf-tools" "7.3.2"
chalk "^4.1.0"
detect-package-manager "^2.0.1"
fetch-retry "^5.0.2"
fs-extra "^11.1.0"
read-pkg-up "^7.0.1"

"@storybook/telemetry@7.4.2":
version "7.4.2"
resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-7.4.2.tgz#cc03efd39661c0d4c8eacfe02e95e90b1cba52a6"
Expand Down