@imposium-hub/components 2.16.0-0 → 2.16.0

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.
@@ -0,0 +1,191 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import React from 'react';
3
+ import { render, fireEvent } from '@testing-library/react';
4
+
5
+ const MOCK_PREVIEW_STYLE = { width: 200, height: 150 };
6
+
7
+ vi.mock('../../index', () => ({
8
+ ASSET_TYPES: {
9
+ VIDEO: 'video',
10
+ IMAGE: 'image',
11
+ AUDIO: 'audio'
12
+ },
13
+ AssetsTypeIcon: ({ type }: any) => (
14
+ <div
15
+ data-testid='assets-type-icon'
16
+ data-type={type}
17
+ />
18
+ ),
19
+ ImagePreview: ({ url, onRequestClose, style }: any) => (
20
+ <div
21
+ data-testid='image-preview'
22
+ data-url={url}
23
+ style={style}>
24
+ <button
25
+ data-testid='image-close'
26
+ onClick={onRequestClose}>
27
+ Close
28
+ </button>
29
+ </div>
30
+ ),
31
+ VideoPreview: ({ url, onRequestClose, playbackSettings, style }: any) => (
32
+ <div
33
+ data-testid='video-preview'
34
+ data-url={url}
35
+ data-autoplay={playbackSettings?.autoPlay}
36
+ data-loop={playbackSettings?.loop}
37
+ style={style}>
38
+ <button
39
+ data-testid='video-close'
40
+ onClick={onRequestClose}>
41
+ Close
42
+ </button>
43
+ </div>
44
+ ),
45
+ AudioPreview: ({ url, onRequestClose, playbackSettings, style }: any) => (
46
+ <div
47
+ data-testid='audio-preview'
48
+ data-url={url}
49
+ data-autoplay={playbackSettings?.autoPlay}
50
+ data-loop={playbackSettings?.loop}
51
+ style={style}>
52
+ <button
53
+ data-testid='audio-close'
54
+ onClick={onRequestClose}>
55
+ Close
56
+ </button>
57
+ </div>
58
+ ),
59
+ getMediaPreviewStyle: vi.fn(() => MOCK_PREVIEW_STYLE)
60
+ }));
61
+
62
+ import AssetsTablePreviewCellMemoized from './AssetsTablePreviewCell';
63
+
64
+ const MOCK_URL = 'https://example.com/asset.mp4';
65
+ const MOCK_WIDTH = 1920;
66
+ const MOCK_HEIGHT = 1080;
67
+
68
+ const buildCellProp = (type: string) => ({
69
+ row: {
70
+ values: { type },
71
+ original: {
72
+ url: MOCK_URL,
73
+ width: MOCK_WIDTH,
74
+ height: MOCK_HEIGHT
75
+ }
76
+ }
77
+ });
78
+
79
+ const renderComponent = (
80
+ type: string,
81
+ showMedia: boolean,
82
+ overrides: { asset?: any; onClick?: () => void; onRequestClose?: () => void } = {}
83
+ ) => {
84
+ const onClick = overrides.onClick ?? vi.fn();
85
+ const onRequestClose = overrides.onRequestClose ?? vi.fn();
86
+ const asset = overrides.asset ?? undefined;
87
+ const cell = buildCellProp(type);
88
+
89
+ const result = render(
90
+ <AssetsTablePreviewCellMemoized
91
+ cell={cell}
92
+ showMedia={showMedia}
93
+ asset={asset}
94
+ onClick={onClick}
95
+ onRequestClose={onRequestClose}
96
+ />
97
+ );
98
+
99
+ return { ...result, onClick, onRequestClose };
100
+ };
101
+
102
+ describe('AssetsTablePreviewCell', () => {
103
+ it('renders AssetsTypeIcon with the correct type', () => {
104
+ const { getByTestId } = renderComponent('video', false);
105
+ const icon = getByTestId('assets-type-icon');
106
+
107
+ expect(icon.getAttribute('data-type')).toBe('video');
108
+ });
109
+
110
+ it('calls onClick when clicked and showMedia is false', () => {
111
+ const onClick = vi.fn();
112
+ const { container } = renderComponent('image', false, { onClick });
113
+ const cell = container.querySelector('.asset-type-cell');
114
+
115
+ if (cell) {
116
+ fireEvent.click(cell);
117
+ }
118
+
119
+ expect(onClick).toHaveBeenCalledOnce();
120
+ });
121
+
122
+ it('does not call onClick when clicked and showMedia is true', () => {
123
+ const onClick = vi.fn();
124
+ const { container } = renderComponent('image', true, { onClick });
125
+ const cell = container.querySelector('.asset-type-cell');
126
+
127
+ if (cell) {
128
+ fireEvent.click(cell);
129
+ }
130
+
131
+ expect(onClick).not.toHaveBeenCalled();
132
+ });
133
+
134
+ it('renders ImagePreview when type is IMAGE and showMedia is true', () => {
135
+ const { getByTestId, queryByTestId } = renderComponent('image', true);
136
+
137
+ expect(getByTestId('image-preview').getAttribute('data-url')).toBe(MOCK_URL);
138
+ expect(queryByTestId('video-preview')).toBeNull();
139
+ expect(queryByTestId('audio-preview')).toBeNull();
140
+ });
141
+
142
+ it('renders VideoPreview when type is VIDEO and showMedia is true', () => {
143
+ const { getByTestId, queryByTestId } = renderComponent('video', true);
144
+
145
+ expect(getByTestId('video-preview').getAttribute('data-url')).toBe(MOCK_URL);
146
+ expect(queryByTestId('image-preview')).toBeNull();
147
+ expect(queryByTestId('audio-preview')).toBeNull();
148
+ });
149
+
150
+ it('renders AudioPreview when type is AUDIO and showMedia is true', () => {
151
+ const { getByTestId, queryByTestId } = renderComponent('audio', true);
152
+
153
+ expect(getByTestId('audio-preview').getAttribute('data-url')).toBe(MOCK_URL);
154
+ expect(queryByTestId('image-preview')).toBeNull();
155
+ expect(queryByTestId('video-preview')).toBeNull();
156
+ });
157
+
158
+ it('does not render any preview when showMedia is false', () => {
159
+ const { queryByTestId } = renderComponent('video', false);
160
+
161
+ expect(queryByTestId('image-preview')).toBeNull();
162
+ expect(queryByTestId('video-preview')).toBeNull();
163
+ expect(queryByTestId('audio-preview')).toBeNull();
164
+ });
165
+
166
+ it('calls onRequestClose when a preview close button is clicked', () => {
167
+ const onRequestClose = vi.fn();
168
+ const { getByTestId } = renderComponent('image', true, { onRequestClose });
169
+
170
+ fireEvent.click(getByTestId('image-close'));
171
+
172
+ expect(onRequestClose).toHaveBeenCalledOnce();
173
+ });
174
+
175
+ it('uses asset url/dimensions when asset prop is provided', () => {
176
+ const customAsset = {
177
+ url: 'https://example.com/custom.png',
178
+ width: 800,
179
+ height: 600
180
+ };
181
+ const { getByTestId } = renderComponent('image', true, { asset: customAsset });
182
+
183
+ expect(getByTestId('image-preview').getAttribute('data-url')).toBe(customAsset.url);
184
+ });
185
+
186
+ it('falls back to cell.row.original values when asset is undefined', () => {
187
+ const { getByTestId } = renderComponent('image', true);
188
+
189
+ expect(getByTestId('image-preview').getAttribute('data-url')).toBe(MOCK_URL);
190
+ });
191
+ });
@@ -0,0 +1,87 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import React from 'react';
3
+ import { render } from '@testing-library/react';
4
+ import AssetsTableRateCell from './AssetsTableRateCell';
5
+
6
+ const VIDEO_COMPOSITION_TYPE = 'video_composition';
7
+ const VIDEO_TYPE = 'video';
8
+
9
+ const buildCellProp = (overrides: Record<string, any> = {}) => ({
10
+ row: {
11
+ original: {
12
+ type: VIDEO_TYPE,
13
+ data: null,
14
+ rate: null,
15
+ ...overrides
16
+ }
17
+ }
18
+ });
19
+
20
+ const renderComponent = (overrides: Record<string, any> = {}) => {
21
+ return render(<AssetsTableRateCell cell={buildCellProp(overrides)} />);
22
+ };
23
+
24
+ describe('AssetsTableRateCell', () => {
25
+ it('renders the rate formatted to two decimal places', () => {
26
+ const { container } = renderComponent({ rate: 29.97 });
27
+ const cell = container.querySelector('.asset-rate-cell');
28
+
29
+ expect(cell?.textContent).toBe('29.97');
30
+ });
31
+
32
+ it('renders an integer rate with two decimal places', () => {
33
+ const { container } = renderComponent({ rate: 30 });
34
+ const cell = container.querySelector('.asset-rate-cell');
35
+
36
+ expect(cell?.textContent).toBe('30.00');
37
+ });
38
+
39
+ it('renders empty when rate is null', () => {
40
+ const { container } = renderComponent({ rate: null });
41
+ const cell = container.querySelector('.asset-rate-cell');
42
+
43
+ expect(cell?.textContent).toBe('');
44
+ });
45
+
46
+ it('renders empty when rate is undefined', () => {
47
+ const { container } = renderComponent({ rate: undefined });
48
+ const cell = container.querySelector('.asset-rate-cell');
49
+
50
+ expect(cell?.textContent).toBe('');
51
+ });
52
+
53
+ it('uses rate from parsed data JSON when type is video_composition', () => {
54
+ const compositionData = JSON.stringify({ rate: 24 });
55
+ const { container } = renderComponent({
56
+ type: VIDEO_COMPOSITION_TYPE,
57
+ data: compositionData,
58
+ rate: 30
59
+ });
60
+ const cell = container.querySelector('.asset-rate-cell');
61
+
62
+ expect(cell?.textContent).toBe('24.00');
63
+ });
64
+
65
+ it('ignores data when type is not video_composition', () => {
66
+ const compositionData = JSON.stringify({ rate: 24 });
67
+ const { container } = renderComponent({
68
+ type: VIDEO_TYPE,
69
+ data: compositionData,
70
+ rate: 60
71
+ });
72
+ const cell = container.querySelector('.asset-rate-cell');
73
+
74
+ expect(cell?.textContent).toBe('60.00');
75
+ });
76
+
77
+ it('does not parse data when data is null even for video_composition type', () => {
78
+ const { container } = renderComponent({
79
+ type: VIDEO_COMPOSITION_TYPE,
80
+ data: null,
81
+ rate: 25
82
+ });
83
+ const cell = container.querySelector('.asset-rate-cell');
84
+
85
+ expect(cell?.textContent).toBe('25.00');
86
+ });
87
+ });
@@ -0,0 +1,156 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import React from 'react';
3
+ import { render, fireEvent } from '@testing-library/react';
4
+ import { Provider } from 'react-redux';
5
+ import { legacy_createStore as createStore } from 'redux';
6
+
7
+ vi.mock('../checkbox-field/CheckboxField', () => ({
8
+ default: ({ label, width, value, onChange }: any) => (
9
+ <div
10
+ data-testid='mock-checkbox-field'
11
+ data-label={label}
12
+ data-width={width}
13
+ data-value={value}>
14
+ <input
15
+ type='checkbox'
16
+ checked={value}
17
+ onChange={(e) => onChange(e.target.checked, e)}
18
+ />
19
+ </div>
20
+ )
21
+ }));
22
+
23
+ vi.mock('../../redux/actions/selected-assets', () => ({
24
+ selectAsset: vi.fn((id) => ({ type: 'selectedAssets/SELECT', id })),
25
+ deselectAsset: vi.fn((id) => ({ type: 'selectedAssets/DESELECT', id })),
26
+ selectMultipleAssets: vi.fn((id) => ({ type: 'selectedAssets/SELECT_MULTIPLE', id }))
27
+ }));
28
+
29
+ import AssetsTableSelectCellMemoized from './AssetsTableSelectCell';
30
+
31
+ const MOCK_ASSET_ID = 'asset-123';
32
+ const MOCK_OTHER_ASSET_ID = 'asset-456';
33
+
34
+ const buildCellProp = (overrides: Record<string, any> = {}) => ({
35
+ row: {
36
+ original: {
37
+ id: MOCK_ASSET_ID,
38
+ type: 'video',
39
+ ...overrides
40
+ }
41
+ }
42
+ });
43
+
44
+ const createMockStore = (selectedAssets: string[] = []) => {
45
+ const initialState = { selectedAssets };
46
+ return createStore((state: any = initialState, action: any) => {
47
+ switch (action.type) {
48
+ case 'selectedAssets/SELECT':
49
+ return {
50
+ ...state,
51
+ selectedAssets: [...state.selectedAssets, action.id]
52
+ };
53
+ case 'selectedAssets/DESELECT':
54
+ return {
55
+ ...state,
56
+ selectedAssets: state.selectedAssets.filter((id) => id !== action.id)
57
+ };
58
+ case 'selectedAssets/SELECT_MULTIPLE':
59
+ return {
60
+ ...state,
61
+ selectedAssets: [...state.selectedAssets, action.id]
62
+ };
63
+ default:
64
+ return state;
65
+ }
66
+ });
67
+ };
68
+
69
+ const renderComponent = (
70
+ cellOverrides: Record<string, any> = {},
71
+ selectedAssets: string[] = []
72
+ ) => {
73
+ const store = createMockStore(selectedAssets);
74
+ const cell = buildCellProp(cellOverrides);
75
+
76
+ const result = render(
77
+ <Provider store={store}>
78
+ <AssetsTableSelectCellMemoized cell={cell} />
79
+ </Provider>
80
+ );
81
+
82
+ return { ...result, store };
83
+ };
84
+
85
+ describe('AssetsTableSelectCell', () => {
86
+ it('renders CheckboxField with correct props when asset is not selected', () => {
87
+ const { getByTestId } = renderComponent();
88
+ const checkbox = getByTestId('mock-checkbox-field');
89
+
90
+ expect(checkbox.getAttribute('data-label')).toBe('');
91
+ expect(checkbox.getAttribute('data-width')).toBe('auto');
92
+ expect(checkbox.getAttribute('data-value')).toBe('false');
93
+ });
94
+
95
+ it('renders CheckboxField as checked when asset is selected', () => {
96
+ const { getByTestId } = renderComponent({}, [MOCK_ASSET_ID]);
97
+ const checkbox = getByTestId('mock-checkbox-field');
98
+
99
+ expect(checkbox.getAttribute('data-value')).toBe('true');
100
+ });
101
+
102
+ it('returns null when asset type is upload', () => {
103
+ const { container } = renderComponent({ type: 'upload' });
104
+
105
+ expect(container.firstChild).toBeNull();
106
+ });
107
+
108
+ it('dispatches selectAsset when checkbox is checked without shift key', () => {
109
+ const { getByRole, store } = renderComponent();
110
+ const checkboxInput = getByRole('checkbox');
111
+
112
+ fireEvent.click(checkboxInput);
113
+
114
+ const storeState = store.getState();
115
+ expect(storeState.selectedAssets).toContain(MOCK_ASSET_ID);
116
+ });
117
+
118
+ it('dispatches deselectAsset when checkbox is unchecked', () => {
119
+ const { getByRole, store } = renderComponent({}, [MOCK_ASSET_ID]);
120
+ const checkboxInput = getByRole('checkbox');
121
+
122
+ fireEvent.click(checkboxInput);
123
+
124
+ const storeState = store.getState();
125
+ expect(storeState.selectedAssets).not.toContain(MOCK_ASSET_ID);
126
+ });
127
+
128
+ it('dispatches selectMultipleAssets when checkbox is checked with shift key and other assets selected', () => {
129
+ const { getByRole, store } = renderComponent({}, [MOCK_OTHER_ASSET_ID]);
130
+ const checkboxInput = getByRole('checkbox');
131
+
132
+ fireEvent.click(checkboxInput, { shiftKey: true });
133
+
134
+ const storeState = store.getState();
135
+ expect(storeState.selectedAssets).toContain(MOCK_ASSET_ID);
136
+ expect(storeState.selectedAssets).toContain(MOCK_OTHER_ASSET_ID);
137
+ });
138
+
139
+ it('dispatches selectAsset when shift key is pressed but no other assets are selected', () => {
140
+ const { getByRole, store } = renderComponent();
141
+ const checkboxInput = getByRole('checkbox');
142
+
143
+ fireEvent.click(checkboxInput, { shiftKey: true });
144
+
145
+ const storeState = store.getState();
146
+ expect(storeState.selectedAssets).toContain(MOCK_ASSET_ID);
147
+ expect(storeState.selectedAssets).toHaveLength(1);
148
+ });
149
+
150
+ it('correctly identifies selected state when asset ID is in selectedAssets array', () => {
151
+ const { getByTestId } = renderComponent({}, [MOCK_OTHER_ASSET_ID, MOCK_ASSET_ID]);
152
+ const checkbox = getByTestId('mock-checkbox-field');
153
+
154
+ expect(checkbox.getAttribute('data-value')).toBe('true');
155
+ });
156
+ });
@@ -0,0 +1,119 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import React from 'react';
3
+ import { render, fireEvent } from '@testing-library/react';
4
+ import { Provider } from 'react-redux';
5
+ import { legacy_createStore as createStore } from 'redux';
6
+
7
+ vi.mock('../checkbox-field/CheckboxField', () => ({
8
+ default: ({ label, propagate, value, onChange }: any) => (
9
+ <div
10
+ data-testid='mock-checkbox-field'
11
+ data-label={label}
12
+ data-propagate={propagate}
13
+ data-value={value}>
14
+ <input
15
+ type='checkbox'
16
+ checked={value}
17
+ onChange={() => onChange()}
18
+ />
19
+ </div>
20
+ )
21
+ }));
22
+
23
+ vi.mock('../../redux/actions/selected-assets', () => ({
24
+ selectAll: vi.fn(() => ({ type: 'selectedAssets/SELECT_ALL' })),
25
+ resetSelection: vi.fn(() => ({ type: 'selectedAssets/RESET' }))
26
+ }));
27
+
28
+ import AssetsTableSelectFilterMemoized from './AssetsTableSelectFilter';
29
+
30
+ const createMockStore = (selectedAssets: string[] = []) => {
31
+ const initialState = { selectedAssets };
32
+ return createStore((state: any = initialState, action: any) => {
33
+ switch (action.type) {
34
+ case 'selectedAssets/SELECT_ALL':
35
+ return {
36
+ ...state,
37
+ selectedAssets: ['asset-1', 'asset-2', 'asset-3']
38
+ };
39
+ case 'selectedAssets/RESET':
40
+ return {
41
+ ...state,
42
+ selectedAssets: []
43
+ };
44
+ default:
45
+ return state;
46
+ }
47
+ });
48
+ };
49
+
50
+ const renderComponent = (selectedAssets: string[] = []) => {
51
+ const store = createMockStore(selectedAssets);
52
+
53
+ const result = render(
54
+ <Provider store={store}>
55
+ <AssetsTableSelectFilterMemoized />
56
+ </Provider>
57
+ );
58
+
59
+ return { ...result, store };
60
+ };
61
+
62
+ describe('AssetsTableSelectFilter', () => {
63
+ it('renders CheckboxField with correct props when no assets are selected', () => {
64
+ const { getByTestId } = renderComponent();
65
+ const checkbox = getByTestId('mock-checkbox-field');
66
+
67
+ expect(checkbox.getAttribute('data-label')).toBe('');
68
+ expect(checkbox.getAttribute('data-propagate')).toBe('false');
69
+ expect(checkbox.getAttribute('data-value')).toBe('false');
70
+ });
71
+
72
+ it('renders CheckboxField as checked when assets are selected', () => {
73
+ const { getByTestId } = renderComponent(['asset-1', 'asset-2']);
74
+ const checkbox = getByTestId('mock-checkbox-field');
75
+
76
+ expect(checkbox.getAttribute('data-value')).toBe('true');
77
+ });
78
+
79
+ it('dispatches selectAll when checkbox is clicked and no assets are selected', () => {
80
+ const { getByRole, store } = renderComponent();
81
+ const checkboxInput = getByRole('checkbox');
82
+
83
+ fireEvent.click(checkboxInput);
84
+
85
+ const storeState = store.getState();
86
+ expect(storeState.selectedAssets).toEqual(['asset-1', 'asset-2', 'asset-3']);
87
+ });
88
+
89
+ it('dispatches resetSelection when checkbox is clicked and assets are selected', () => {
90
+ const { getByRole, store } = renderComponent(['asset-1', 'asset-2']);
91
+ const checkboxInput = getByRole('checkbox');
92
+
93
+ fireEvent.click(checkboxInput);
94
+
95
+ const storeState = store.getState();
96
+ expect(storeState.selectedAssets).toEqual([]);
97
+ });
98
+
99
+ it('shows unchecked state when selectedAssets array is empty', () => {
100
+ const { getByTestId } = renderComponent([]);
101
+ const checkbox = getByTestId('mock-checkbox-field');
102
+
103
+ expect(checkbox.getAttribute('data-value')).toBe('false');
104
+ });
105
+
106
+ it('shows checked state when selectedAssets array has one item', () => {
107
+ const { getByTestId } = renderComponent(['single-asset']);
108
+ const checkbox = getByTestId('mock-checkbox-field');
109
+
110
+ expect(checkbox.getAttribute('data-value')).toBe('true');
111
+ });
112
+
113
+ it('shows checked state when selectedAssets array has multiple items', () => {
114
+ const { getByTestId } = renderComponent(['asset-1', 'asset-2', 'asset-3']);
115
+ const checkbox = getByTestId('mock-checkbox-field');
116
+
117
+ expect(checkbox.getAttribute('data-value')).toBe('true');
118
+ });
119
+ });
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import React from 'react';
3
+ import { render } from '@testing-library/react';
4
+ import AssetsTableStatusCellMemoized from './AssetsTableStatusCell';
5
+
6
+ const buildCellProp = (overrides: Record<string, any> = {}) => ({
7
+ row: {
8
+ original: {
9
+ processing: false,
10
+ ...overrides
11
+ }
12
+ }
13
+ });
14
+
15
+ const renderComponent = (overrides: Record<string, any> = {}) => {
16
+ return render(<AssetsTableStatusCellMemoized cell={buildCellProp(overrides)} />);
17
+ };
18
+
19
+ describe('AssetsTableStatusCell', () => {
20
+ it('renders with complete status when processing is false', () => {
21
+ const { container } = renderComponent({ processing: false });
22
+ const statusCell = container.querySelector('.asset-status-cell');
23
+ const statusIndicator = container.querySelector('.status-indicator');
24
+
25
+ expect(statusCell).toBeTruthy();
26
+ expect(statusIndicator?.className).toBe('status-indicator complete');
27
+ });
28
+
29
+ it('renders with processing status when processing is true', () => {
30
+ const { container } = renderComponent({ processing: true });
31
+ const statusIndicator = container.querySelector('.status-indicator');
32
+
33
+ expect(statusIndicator?.className).toBe('status-indicator processing');
34
+ });
35
+
36
+ it('renders with complete status when processing is undefined', () => {
37
+ const { container } = renderComponent({ processing: undefined });
38
+ const statusIndicator = container.querySelector('.status-indicator');
39
+
40
+ expect(statusIndicator?.className).toBe('status-indicator complete');
41
+ });
42
+
43
+ it('renders with complete status when processing is null', () => {
44
+ const { container } = renderComponent({ processing: null });
45
+ const statusIndicator = container.querySelector('.status-indicator');
46
+
47
+ expect(statusIndicator?.className).toBe('status-indicator complete');
48
+ });
49
+
50
+ it('renders the correct DOM structure', () => {
51
+ const { container } = renderComponent();
52
+ const statusCell = container.querySelector('.asset-status-cell');
53
+ const statusIndicator = statusCell?.querySelector('.status-indicator');
54
+
55
+ expect(statusCell).toBeTruthy();
56
+ expect(statusIndicator).toBeTruthy();
57
+ expect(statusCell?.children).toHaveLength(1);
58
+ expect(statusCell?.firstChild).toEqual(statusIndicator);
59
+ });
60
+ });
@@ -393,7 +393,12 @@ class PublishWizard extends React.PureComponent<IPublishWizardProps, IPublishWiz
393
393
  }
394
394
 
395
395
  private getErrorCopyForCrMPublish() {
396
- const { story, project } = this.props;
396
+ const { story, project, fromCrM } = this.props;
397
+
398
+ if (!fromCrM) {
399
+ return null;
400
+ }
401
+
397
402
  const variables = story ? story.acts[project.actId].inventory : {};
398
403
  const varLength = variables ? Object.keys(variables).length : 0;
399
404
 
@@ -574,6 +579,7 @@ class PublishWizard extends React.PureComponent<IPublishWizardProps, IPublishWiz
574
579
  const projectType = story.creativeId
575
580
  ? CRM_INTEGRATED_PROJECT_TYPES.SINGLE_CREATIVE
576
581
  : CRM_INTEGRATED_PROJECT_TYPES.MULTI_CREATIVE;
582
+
577
583
  const error = errorCopy ? (
578
584
  <p className='publish-error'>
579
585
  <span className='icon'>{ICON_EXCLAIMATION_TRIANGLE}</span>&nbsp;&nbsp;{errorCopy}