@capillarytech/creatives-library 8.0.318 → 8.0.319
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/constants/unified.js +1 -0
- package/package.json +1 -1
- package/services/api.js +6 -0
- package/services/tests/api.test.js +7 -0
- package/utils/common.js +6 -1
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
- package/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
- package/v2Containers/CommunicationFlow/constants.js +200 -0
- package/v2Containers/CommunicationFlow/index.js +102 -0
- package/v2Containers/CommunicationFlow/messages.js +346 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
- package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
- package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
- package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
- package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
- package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
- package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
- package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
- package/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
- package/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
- package/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
- package/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
- package/v2Containers/CreativesContainer/constants.js +3 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeliverySettingsStep - Entry point
|
|
3
|
+
* Exports: DeliverySettingsSection (integrated in ChannelSelectionStep), SenderDetails
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { default as SenderDetails, parseSenderDetailsFromEntity } from './SenderDetails';
|
|
7
|
+
export { default as DeliverySettingsSection } from './DeliverySettingsSection';
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicControlsStep Component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import PropTypes from 'prop-types';
|
|
7
|
+
import { injectIntl } from 'react-intl';
|
|
8
|
+
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
9
|
+
import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
|
|
10
|
+
import CapSwitch from '@capillarytech/cap-ui-library/CapSwitch';
|
|
11
|
+
import messages from '../../messages';
|
|
12
|
+
import './DynamicControlsStep.scss';
|
|
13
|
+
|
|
14
|
+
const DynamicControlsStep = ({
|
|
15
|
+
value,
|
|
16
|
+
onChange,
|
|
17
|
+
controls,
|
|
18
|
+
error,
|
|
19
|
+
intl,
|
|
20
|
+
}) => {
|
|
21
|
+
const { formatMessage } = intl || {};
|
|
22
|
+
// formatMessage used for section title only
|
|
23
|
+
const dynamicControls = value?.dynamicControls || {};
|
|
24
|
+
|
|
25
|
+
const handleToggle = (key, checked) => {
|
|
26
|
+
onChange({
|
|
27
|
+
...value,
|
|
28
|
+
dynamicControls: { ...dynamicControls, [key]: checked },
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
if (!controls?.length) return null;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<CapRow className="dynamic-controls-step">
|
|
36
|
+
<CapHeading type="h3" className="heading-style">
|
|
37
|
+
{formatMessage(messages.dynamicControlsTitle)}
|
|
38
|
+
</CapHeading>
|
|
39
|
+
|
|
40
|
+
{controls.map(({ key, label, description }) => {
|
|
41
|
+
const checked = !!dynamicControls[key];
|
|
42
|
+
const displayLabel = label || key;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<CapRow
|
|
46
|
+
key={key}
|
|
47
|
+
type="flex"
|
|
48
|
+
align="top"
|
|
49
|
+
justify="space-between"
|
|
50
|
+
className="dynamic-controls-step__row"
|
|
51
|
+
>
|
|
52
|
+
<CapRow className="dynamic-controls-step__label-wrap">
|
|
53
|
+
<CapHeading type="h4" className="dynamic-controls-step__label">
|
|
54
|
+
{displayLabel}
|
|
55
|
+
</CapHeading>
|
|
56
|
+
{description && (
|
|
57
|
+
<CapHeading type="label4" className="dynamic-controls-step__desc">
|
|
58
|
+
{description}
|
|
59
|
+
</CapHeading>
|
|
60
|
+
)}
|
|
61
|
+
</CapRow>
|
|
62
|
+
<CapSwitch
|
|
63
|
+
checked={checked}
|
|
64
|
+
onChange={(val) => handleToggle(key, val)}
|
|
65
|
+
className="dynamic-controls-step__switch"
|
|
66
|
+
/>
|
|
67
|
+
</CapRow>
|
|
68
|
+
);
|
|
69
|
+
})}
|
|
70
|
+
|
|
71
|
+
{error && (
|
|
72
|
+
<CapRow className="validation-error dynamic-controls-step__error">
|
|
73
|
+
{error}
|
|
74
|
+
</CapRow>
|
|
75
|
+
)}
|
|
76
|
+
</CapRow>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
DynamicControlsStep.propTypes = {
|
|
81
|
+
value: PropTypes.shape({
|
|
82
|
+
dynamicControls: PropTypes.object,
|
|
83
|
+
}),
|
|
84
|
+
onChange: PropTypes.func.isRequired,
|
|
85
|
+
controls: PropTypes.arrayOf(
|
|
86
|
+
PropTypes.shape({
|
|
87
|
+
key: PropTypes.string.isRequired,
|
|
88
|
+
label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
|
89
|
+
description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
|
90
|
+
}),
|
|
91
|
+
),
|
|
92
|
+
error: PropTypes.string,
|
|
93
|
+
intl: PropTypes.object.isRequired,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
DynamicControlsStep.defaultProps = {
|
|
97
|
+
value: {},
|
|
98
|
+
controls: [],
|
|
99
|
+
error: null,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export default injectIntl(DynamicControlsStep);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
@import '~@capillarytech/cap-ui-library/styles/_variables.scss';
|
|
2
|
+
|
|
3
|
+
.dynamic-controls-step {
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
|
|
6
|
+
&__row {
|
|
7
|
+
padding: $CAP_SPACE_16 0;
|
|
8
|
+
align-items: flex-start;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
&__label-wrap {
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
align-items: flex-start;
|
|
14
|
+
flex: 1;
|
|
15
|
+
padding-right: $CAP_SPACE_24;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&__label {
|
|
19
|
+
line-height: $CAP_SPACE_20;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
&__desc {
|
|
23
|
+
margin-top: $CAP_SPACE_04;
|
|
24
|
+
color: $FONT_COLOR_03;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
&__switch {
|
|
28
|
+
flex-shrink: 0;
|
|
29
|
+
margin-top: 0.125rem;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&__error {
|
|
33
|
+
margin-top: $CAP_SPACE_08;
|
|
34
|
+
color: $CAP_RED;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
render, screen,
|
|
4
|
+
} from '@testing-library/react';
|
|
5
|
+
import userEvent from '@testing-library/user-event';
|
|
6
|
+
import '@testing-library/jest-dom';
|
|
7
|
+
import { IntlProvider } from 'react-intl';
|
|
8
|
+
import DynamicControlsStep from '../DynamicControlsStep';
|
|
9
|
+
|
|
10
|
+
const SAMPLE_CONTROLS = [
|
|
11
|
+
{ key: 'alpha', label: 'Alpha', description: 'Alpha help' },
|
|
12
|
+
{ key: 'beta', label: 'Beta', description: 'Beta help' },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
function renderWithIntl(ui) {
|
|
16
|
+
return render(
|
|
17
|
+
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
|
18
|
+
{ui}
|
|
19
|
+
</IntlProvider>,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function DynamicControlsHarness({ initialDynamicControls = {} }) {
|
|
24
|
+
const [value, setValue] = useState({ dynamicControls: initialDynamicControls });
|
|
25
|
+
return (
|
|
26
|
+
<DynamicControlsStep
|
|
27
|
+
value={value}
|
|
28
|
+
onChange={setValue}
|
|
29
|
+
controls={SAMPLE_CONTROLS}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('DynamicControlsStep', () => {
|
|
35
|
+
it('renders nothing when there are no controls', () => {
|
|
36
|
+
const { container } = renderWithIntl(
|
|
37
|
+
<DynamicControlsStep
|
|
38
|
+
value={{}}
|
|
39
|
+
onChange={jest.fn()}
|
|
40
|
+
controls={[]}
|
|
41
|
+
/>,
|
|
42
|
+
);
|
|
43
|
+
expect(container.firstChild).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('shows each switch on (true) or off (false) from dynamicControls for every field', () => {
|
|
47
|
+
renderWithIntl(
|
|
48
|
+
<DynamicControlsHarness initialDynamicControls={{ alpha: true, beta: false }} />,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
expect(screen.getByText('Other controls')).toBeInTheDocument();
|
|
52
|
+
expect(screen.getByText('Alpha')).toBeInTheDocument();
|
|
53
|
+
expect(screen.getByText('Beta')).toBeInTheDocument();
|
|
54
|
+
|
|
55
|
+
const switches = screen.getAllByRole('switch');
|
|
56
|
+
expect(switches).toHaveLength(2);
|
|
57
|
+
expect(switches[0]).toBeChecked();
|
|
58
|
+
expect(switches[1]).not.toBeChecked();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('toggles every control through false and true via onChange', async () => {
|
|
62
|
+
renderWithIntl(
|
|
63
|
+
<DynamicControlsHarness initialDynamicControls={{ alpha: true, beta: false }} />,
|
|
64
|
+
);
|
|
65
|
+
const switches = screen.getAllByRole('switch');
|
|
66
|
+
|
|
67
|
+
await userEvent.click(switches[0]);
|
|
68
|
+
expect(switches[0]).not.toBeChecked();
|
|
69
|
+
|
|
70
|
+
await userEvent.click(switches[1]);
|
|
71
|
+
expect(switches[1]).toBeChecked();
|
|
72
|
+
|
|
73
|
+
await userEvent.click(switches[0]);
|
|
74
|
+
expect(switches[0]).toBeChecked();
|
|
75
|
+
|
|
76
|
+
await userEvent.click(switches[1]);
|
|
77
|
+
expect(switches[1]).not.toBeChecked();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('renders validation error when error is set', () => {
|
|
81
|
+
renderWithIntl(
|
|
82
|
+
<DynamicControlsStep
|
|
83
|
+
value={{ dynamicControls: { alpha: false } }}
|
|
84
|
+
onChange={jest.fn()}
|
|
85
|
+
controls={[SAMPLE_CONTROLS[0]]}
|
|
86
|
+
error="Dynamic controls validation failed"
|
|
87
|
+
/>,
|
|
88
|
+
);
|
|
89
|
+
expect(screen.getByText('Dynamic controls validation failed')).toBeInTheDocument();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MessageTypeStep Component
|
|
3
|
+
*
|
|
4
|
+
* Radio group for selecting Message Type: Promotional | Transactional
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
9
|
+
import { injectIntl } from 'react-intl';
|
|
10
|
+
import CapRadio from '@capillarytech/cap-ui-library/CapRadio';
|
|
11
|
+
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
12
|
+
import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
|
|
13
|
+
import { CAP_SPACE_24 } from '@capillarytech/cap-ui-library/styled/variables';
|
|
14
|
+
import { MESSAGE_TYPES_OPTIONS } from '../../constants';
|
|
15
|
+
import messages from '../../messages';
|
|
16
|
+
import '../../CommunicationFlow.scss';
|
|
17
|
+
|
|
18
|
+
const MessageTypeStep = ({
|
|
19
|
+
value,
|
|
20
|
+
defaultOption,
|
|
21
|
+
options: optionsProp,
|
|
22
|
+
onChange,
|
|
23
|
+
error,
|
|
24
|
+
intl,
|
|
25
|
+
}) => {
|
|
26
|
+
const { formatMessage } = intl || {};
|
|
27
|
+
const options = optionsProp || MESSAGE_TYPES_OPTIONS;
|
|
28
|
+
|
|
29
|
+
// Use defaultOption value if value is null/undefined
|
|
30
|
+
const selectedValue = value || defaultOption?.value;
|
|
31
|
+
|
|
32
|
+
const handleChange = (messageType) => {
|
|
33
|
+
onChange(messageType);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<CapRow className="message-type-step">
|
|
38
|
+
<CapHeading type="h3" className="heading-style">
|
|
39
|
+
{formatMessage(messages.messageTypeHeading)}
|
|
40
|
+
</CapHeading>
|
|
41
|
+
|
|
42
|
+
<CapRow align="middle" type="flex" gap={CAP_SPACE_24}>
|
|
43
|
+
{options?.map((option) => (
|
|
44
|
+
<CapRadio
|
|
45
|
+
key={option?.value}
|
|
46
|
+
checked={selectedValue === option?.value}
|
|
47
|
+
onChange={() => handleChange(option?.value)}
|
|
48
|
+
disabled={option?.disabled}
|
|
49
|
+
>
|
|
50
|
+
{option?.label}
|
|
51
|
+
</CapRadio>
|
|
52
|
+
))}
|
|
53
|
+
</CapRow>
|
|
54
|
+
|
|
55
|
+
{error && (
|
|
56
|
+
<CapRow className="validation-error">
|
|
57
|
+
{error}
|
|
58
|
+
</CapRow>
|
|
59
|
+
)}
|
|
60
|
+
</CapRow>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
MessageTypeStep.propTypes = {
|
|
65
|
+
value: PropTypes.oneOf(['promotional', 'transactional']),
|
|
66
|
+
defaultOption: PropTypes.shape({
|
|
67
|
+
value: PropTypes.string.isRequired,
|
|
68
|
+
label: PropTypes.node.isRequired,
|
|
69
|
+
}),
|
|
70
|
+
options: PropTypes.arrayOf(PropTypes.shape({
|
|
71
|
+
value: PropTypes.string.isRequired,
|
|
72
|
+
label: PropTypes.node.isRequired,
|
|
73
|
+
})),
|
|
74
|
+
onChange: PropTypes.func.isRequired,
|
|
75
|
+
error: PropTypes.string,
|
|
76
|
+
intl: PropTypes.object.isRequired,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
MessageTypeStep.defaultProps = {
|
|
80
|
+
value: null,
|
|
81
|
+
defaultOption: null,
|
|
82
|
+
options: null,
|
|
83
|
+
error: null,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default injectIntl(MessageTypeStep);
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import '@testing-library/jest-dom';
|
|
5
|
+
import { IntlProvider } from 'react-intl';
|
|
6
|
+
import MessageTypeStep from '../MessageTypeStep';
|
|
7
|
+
|
|
8
|
+
const OPTIONS = [
|
|
9
|
+
{ value: 'promotional', label: 'Promotional', disabled: false },
|
|
10
|
+
{ value: 'transactional', label: 'Transactional', disabled: false },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
function renderWithIntl(ui) {
|
|
14
|
+
return render(
|
|
15
|
+
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
|
16
|
+
{ui}
|
|
17
|
+
</IntlProvider>,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('MessageTypeStep', () => {
|
|
22
|
+
it('shows the step heading and one radio per option', () => {
|
|
23
|
+
const onChange = jest.fn();
|
|
24
|
+
renderWithIntl(
|
|
25
|
+
<MessageTypeStep
|
|
26
|
+
value="promotional"
|
|
27
|
+
options={OPTIONS}
|
|
28
|
+
defaultOption={{ value: 'promotional', label: 'Promotional' }}
|
|
29
|
+
onChange={onChange}
|
|
30
|
+
/>,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
expect(screen.getByText(/message type/i)).toBeInTheDocument();
|
|
34
|
+
expect(screen.getByRole('radio', { name: /promotional/i })).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByRole('radio', { name: /transactional/i })).toBeInTheDocument();
|
|
36
|
+
expect(screen.getByRole('radio', { name: /promotional/i })).toBeChecked();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('uses defaultOption when value is null so the default appears selected', () => {
|
|
40
|
+
const onChange = jest.fn();
|
|
41
|
+
renderWithIntl(
|
|
42
|
+
<MessageTypeStep
|
|
43
|
+
value={null}
|
|
44
|
+
options={OPTIONS}
|
|
45
|
+
defaultOption={{ value: 'transactional', label: 'Transactional' }}
|
|
46
|
+
onChange={onChange}
|
|
47
|
+
/>,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(screen.getByRole('radio', { name: /transactional/i })).toBeChecked();
|
|
51
|
+
expect(screen.getByRole('radio', { name: /promotional/i })).not.toBeChecked();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('calls onChange with the chosen message type when the user switches radios', async () => {
|
|
55
|
+
const onChange = jest.fn();
|
|
56
|
+
renderWithIntl(
|
|
57
|
+
<MessageTypeStep
|
|
58
|
+
value="promotional"
|
|
59
|
+
options={OPTIONS}
|
|
60
|
+
defaultOption={{ value: 'promotional', label: 'Promotional' }}
|
|
61
|
+
onChange={onChange}
|
|
62
|
+
/>,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
await userEvent.click(screen.getByRole('radio', { name: /transactional/i }));
|
|
66
|
+
expect(onChange).toHaveBeenCalledWith('transactional');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('disables individual options when configured', () => {
|
|
70
|
+
const onChange = jest.fn();
|
|
71
|
+
const withDisabled = [
|
|
72
|
+
{ value: 'promotional', label: 'Promotional', disabled: true },
|
|
73
|
+
{ value: 'transactional', label: 'Transactional', disabled: false },
|
|
74
|
+
];
|
|
75
|
+
renderWithIntl(
|
|
76
|
+
<MessageTypeStep
|
|
77
|
+
value="transactional"
|
|
78
|
+
options={withDisabled}
|
|
79
|
+
onChange={onChange}
|
|
80
|
+
/>,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(screen.getByRole('radio', { name: /promotional/i })).toBeDisabled();
|
|
84
|
+
expect(screen.getByRole('radio', { name: /transactional/i })).not.toBeDisabled();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('shows validation error text below the radios', () => {
|
|
88
|
+
const onChange = jest.fn();
|
|
89
|
+
renderWithIntl(
|
|
90
|
+
<MessageTypeStep
|
|
91
|
+
value="promotional"
|
|
92
|
+
options={OPTIONS}
|
|
93
|
+
onChange={onChange}
|
|
94
|
+
error="Please choose a message type"
|
|
95
|
+
/>,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
expect(screen.getByText('Please choose a message type')).toBeInTheDocument();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { STEPS } from '../constants';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Enabled steps in order from config.features.*.required flags.
|
|
5
|
+
* Channel selection, incentives, delivery, and dynamic controls are toggled independently.
|
|
6
|
+
*/
|
|
7
|
+
export const getEnabledSteps = (config) => {
|
|
8
|
+
const { features = {} } = config;
|
|
9
|
+
const steps = [];
|
|
10
|
+
|
|
11
|
+
if (features.messageTypeData?.required) {
|
|
12
|
+
steps.push(STEPS.MESSAGE_TYPE);
|
|
13
|
+
}
|
|
14
|
+
if (features.communicationStrategyData?.required) {
|
|
15
|
+
steps.push(STEPS.COMMUNICATION_STRATEGY);
|
|
16
|
+
}
|
|
17
|
+
if (features.contentTemplateData?.required) {
|
|
18
|
+
steps.push(STEPS.CHANNEL_SELECTION);
|
|
19
|
+
}
|
|
20
|
+
if (features.incentivesData?.required) {
|
|
21
|
+
steps.push(STEPS.INCENTIVES);
|
|
22
|
+
}
|
|
23
|
+
if (features.deliverySettingsData?.required) {
|
|
24
|
+
steps.push(STEPS.DELIVERY_SETTINGS);
|
|
25
|
+
}
|
|
26
|
+
if (features.dynamicControlsData?.required) {
|
|
27
|
+
steps.push(STEPS.DYNAMIC_CONTROLS);
|
|
28
|
+
}
|
|
29
|
+
return steps;
|
|
30
|
+
};
|
|
@@ -11,6 +11,7 @@ export const EBILL = "EBILL";
|
|
|
11
11
|
export const LINE = "LINE";
|
|
12
12
|
export const CALL_TASK = "CALL_TASK";
|
|
13
13
|
export const MOBILE_PUSH = "MOBILEPUSH";
|
|
14
|
+
export const MPUSH = "MPUSH";
|
|
14
15
|
export const WECHAT = "WECHAT";
|
|
15
16
|
export const FACEBOOK = "FACEBOOK";
|
|
16
17
|
export const FTP = "FTP";
|
|
@@ -57,6 +58,8 @@ export const COMMON_CHANNELS = ['sms', 'email', 'wechat', 'mobilepush', 'webpush
|
|
|
57
58
|
export const NORMALIZED_CHANNEL_ALIASES = {
|
|
58
59
|
we_chat: WECHAT.toLowerCase(),
|
|
59
60
|
m_push: MOBILE_PUSH.toLowerCase(),
|
|
61
|
+
// paneKey `inApp` → `in_app`; TemplatesV2 pane uses key INAPP → `inapp` — both must match for channelsToHide
|
|
62
|
+
in_app: INAPP.toLowerCase(),
|
|
60
63
|
};
|
|
61
64
|
export const MIXED = "MIXED";
|
|
62
65
|
export const VISITOR = "VISITOR";
|