@imposium-hub/components 2.15.0-2 → 2.16.0-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.
- package/dist/cjs/components/app-wrapper/AppWrapper.d.ts +0 -1
- package/dist/cjs/components/app-wrapper/AppWrapper.js +1 -1
- package/dist/cjs/components/app-wrapper/AppWrapper.js.map +1 -1
- package/dist/cjs/components/asset-details/AssetDetails.js +1 -1
- package/dist/cjs/components/assets/AssetsTableComplexTagCell.js +1 -1
- package/dist/cjs/components/assets/AssetsTableTagsPivot.js +1 -1
- package/dist/cjs/components/assets/AssetsTypeIcon.js +2 -0
- package/dist/cjs/components/assets/AssetsTypeIcon.js.map +1 -1
- package/dist/cjs/components/assets/AssetsUploadMenu.js +1 -1
- package/dist/cjs/components/change-report/ChangeReportTree.js +65 -10
- package/dist/cjs/components/change-report/ChangeReportTree.js.map +1 -1
- package/dist/cjs/components/controlled-list/ControlledList.stories.js +1 -1
- package/dist/cjs/components/number-field/NumberField.d.ts +2 -2
- package/dist/cjs/components/number-field/NumberField.js +7 -4
- package/dist/cjs/components/number-field/NumberField.js.map +1 -1
- package/dist/cjs/components/publish-wizard/PublishWizard.js +36 -38
- package/dist/cjs/components/publish-wizard/PublishWizard.js.map +1 -1
- package/dist/cjs/components/tag/Tag.js.map +1 -1
- package/dist/cjs/components/tag/Tag.test.js.map +1 -1
- package/dist/cjs/constants/copy.d.ts +1 -0
- package/dist/cjs/constants/copy.js +3 -2
- package/dist/cjs/constants/copy.js.map +1 -1
- package/dist/cjs/constants/icons.d.ts +5 -0
- package/dist/cjs/constants/icons.js +12 -2
- package/dist/cjs/constants/icons.js.map +1 -1
- package/dist/cjs/constants/snippets.d.ts +2 -0
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/redux/actions/publish.d.ts +1 -1
- package/dist/cjs/redux/actions/publish.js +2 -2
- package/dist/cjs/redux/actions/publish.js.map +1 -1
- package/dist/esm/components/app-wrapper/AppWrapper.d.ts +0 -1
- package/dist/esm/components/app-wrapper/AppWrapper.js +1 -1
- package/dist/esm/components/app-wrapper/AppWrapper.js.map +1 -1
- package/dist/esm/components/asset-details/AssetDetails.js +1 -1
- package/dist/esm/components/assets/AssetsTableComplexTagCell.js +1 -1
- package/dist/esm/components/assets/AssetsTableTagsPivot.js +1 -1
- package/dist/esm/components/assets/AssetsTypeIcon.js +2 -0
- package/dist/esm/components/assets/AssetsTypeIcon.js.map +1 -1
- package/dist/esm/components/assets/AssetsUploadMenu.js +1 -1
- package/dist/esm/components/change-report/ChangeReportTree.js +64 -10
- package/dist/esm/components/change-report/ChangeReportTree.js.map +1 -1
- package/dist/esm/components/controlled-list/ControlledList.stories.js +1 -1
- package/dist/esm/components/number-field/NumberField.d.ts +2 -2
- package/dist/esm/components/number-field/NumberField.js +5 -4
- package/dist/esm/components/number-field/NumberField.js.map +1 -1
- package/dist/esm/components/publish-wizard/PublishWizard.js +36 -38
- package/dist/esm/components/publish-wizard/PublishWizard.js.map +1 -1
- package/dist/esm/components/tag/Tag.js.map +1 -1
- package/dist/esm/components/tag/Tag.test.js.map +1 -1
- package/dist/esm/constants/copy.d.ts +1 -0
- package/dist/esm/constants/copy.js +3 -2
- package/dist/esm/constants/copy.js.map +1 -1
- package/dist/esm/constants/icons.d.ts +5 -0
- package/dist/esm/constants/icons.js +10 -0
- package/dist/esm/constants/icons.js.map +1 -1
- package/dist/esm/constants/snippets.d.ts +2 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/redux/actions/publish.d.ts +1 -1
- package/dist/esm/redux/actions/publish.js +2 -2
- package/dist/esm/redux/actions/publish.js.map +1 -1
- package/dist/styles.css +54 -10
- package/dist/styles.less +65 -13
- package/less/components/change-report.less +50 -9
- package/less/components/publish-wizard.less +15 -4
- package/package.json +5 -1
- package/src/components/advanced-number-field/AdvancedNumberField.test.tsx +704 -0
- package/src/components/anchor-field/AnchorField.test.tsx +130 -0
- package/src/components/app-wrapper/AppWrapper.tsx +1 -6
- package/src/components/asset-details/AssetDetails.test.tsx +494 -0
- package/src/components/asset-details/AssetDetails.tsx +1 -1
- package/src/components/assets/AssetField.test.tsx +439 -0
- package/src/components/assets/AssetsTableAssetIdCell.test.tsx +134 -0
- package/src/components/assets/AssetsTableAssetIdFilter.test.tsx +95 -0
- package/src/components/assets/AssetsTableComplexTagCell.test.tsx +159 -0
- package/src/components/assets/AssetsTableComplexTagCell.tsx +1 -1
- package/src/components/assets/AssetsTableDateCell.test.tsx +106 -0
- package/src/components/assets/AssetsTableTagsPivot.tsx +1 -1
- package/src/components/assets/AssetsTypeIcon.tsx +2 -0
- package/src/components/assets/AssetsUploadMenu.tsx +1 -1
- package/src/components/change-report/ChangeReportTree.tsx +104 -16
- package/src/components/controlled-list/ControlledList.stories.tsx +1 -1
- package/src/components/number-field/NumberField.test.tsx +383 -0
- package/src/components/number-field/NumberField.tsx +15 -9
- package/src/components/publish-wizard/PublishWizard.tsx +52 -53
- package/src/components/text-field/TextField.test.tsx +988 -0
- package/src/constants/copy.ts +3 -2
- package/src/constants/icons.tsx +10 -0
- package/src/constants/snippets.ts +2 -0
- package/src/index.ts +1 -1
- package/src/redux/actions/publish.ts +7 -2
- package/src/test/setup.ts +91 -0
- package/src/test/utils.tsx +44 -0
- package/tsconfig.eslint.json +8 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +31 -0
- package/src/components/service-icon/ServiceIcon.test.tsx +0 -0
- /package/src/components/{Tag → tag}/Tag.test.tsx +0 -0
- /package/src/components/{Tag → tag}/Tag.tsx +0 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
import { Provider } from 'react-redux';
|
|
5
|
+
import { legacy_createStore as createStore } from 'redux';
|
|
6
|
+
|
|
7
|
+
let capturedDoSubmit: (value: string) => void;
|
|
8
|
+
let capturedRemoveHandlers: Record<string, () => void> = {};
|
|
9
|
+
|
|
10
|
+
vi.mock('../tag/Tag', () => ({
|
|
11
|
+
default: ({ copy, removeHandler }: any) => {
|
|
12
|
+
capturedRemoveHandlers[copy] = removeHandler;
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
data-testid={`tag-${copy}`}
|
|
16
|
+
className='mock-tag'>
|
|
17
|
+
<span>{copy}</span>
|
|
18
|
+
<button
|
|
19
|
+
data-testid={`remove-${copy}`}
|
|
20
|
+
onClick={removeHandler}>
|
|
21
|
+
x
|
|
22
|
+
</button>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
vi.mock('../text-field/TextField', () => ({
|
|
29
|
+
default: ({ submittable, submittableType, placeholder, value, doSubmit, suggestions }: any) => {
|
|
30
|
+
capturedDoSubmit = doSubmit;
|
|
31
|
+
return (
|
|
32
|
+
<div
|
|
33
|
+
data-testid='mock-text-field'
|
|
34
|
+
data-submittable={submittable}
|
|
35
|
+
data-submittable-type={submittableType}
|
|
36
|
+
data-placeholder={placeholder}
|
|
37
|
+
data-value={value}
|
|
38
|
+
data-suggestions={JSON.stringify(suggestions || [])}>
|
|
39
|
+
<button
|
|
40
|
+
data-testid='submit-trigger'
|
|
41
|
+
onClick={() => doSubmit('new-tag')}>
|
|
42
|
+
Add
|
|
43
|
+
</button>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
vi.mock('../../redux/actions/asset-list', () => ({
|
|
50
|
+
addAssetTag: vi.fn((_api, _id, _tags) => ({ type: 'MOCK_ADD_TAG' })),
|
|
51
|
+
deleteAssetTag: vi.fn((_api, _id, _tag) => ({ type: 'MOCK_DELETE_TAG' })),
|
|
52
|
+
default: {}
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
import AssetsTableComplexTagCellMemoized from './AssetsTableComplexTagCell';
|
|
56
|
+
import { addAssetTag, deleteAssetTag } from '../../redux/actions/asset-list';
|
|
57
|
+
|
|
58
|
+
const MOCK_ASSET_ID = 'asset-999';
|
|
59
|
+
const MOCK_API = { addAssetTag: vi.fn(), deleteAssetTag: vi.fn() };
|
|
60
|
+
const MOCK_SUGGESTIONS = ['alpha', 'beta', 'gamma'];
|
|
61
|
+
|
|
62
|
+
const createMockStore = (assetTags: string[] = MOCK_SUGGESTIONS) => {
|
|
63
|
+
const initialState = { assetTags };
|
|
64
|
+
return createStore((state = initialState) => state);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const buildCellProp = (tags: string[] = ['tag-a', 'tag-b']) => ({
|
|
68
|
+
row: {
|
|
69
|
+
original: { id: MOCK_ASSET_ID, tags }
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const renderComponent = (tagOverrides?: string[], storeOverrides?: string[]) => {
|
|
74
|
+
capturedRemoveHandlers = {};
|
|
75
|
+
const store = createMockStore(storeOverrides);
|
|
76
|
+
const cell = buildCellProp(tagOverrides);
|
|
77
|
+
|
|
78
|
+
const result = render(
|
|
79
|
+
<Provider store={store}>
|
|
80
|
+
<AssetsTableComplexTagCellMemoized
|
|
81
|
+
cell={cell}
|
|
82
|
+
api={MOCK_API}
|
|
83
|
+
/>
|
|
84
|
+
</Provider>
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return { ...result, store };
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
describe('AssetsTableComplexTagCell', () => {
|
|
91
|
+
it('renders existing tags as Tag components', () => {
|
|
92
|
+
const tags = ['tag-a', 'tag-b', 'tag-c'];
|
|
93
|
+
const { getByTestId } = renderComponent(tags);
|
|
94
|
+
|
|
95
|
+
tags.forEach((tag) => {
|
|
96
|
+
expect(getByTestId(`tag-${tag}`)).toBeTruthy();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('deduplicates tags before rendering', () => {
|
|
101
|
+
const { container } = renderComponent(['dup', 'dup', 'unique']);
|
|
102
|
+
const renderedTags = container.querySelectorAll('.mock-tag');
|
|
103
|
+
|
|
104
|
+
expect(renderedTags).toHaveLength(2);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('renders the TextField with correct props', () => {
|
|
108
|
+
const { getByTestId } = renderComponent();
|
|
109
|
+
const textField = getByTestId('mock-text-field');
|
|
110
|
+
|
|
111
|
+
expect(textField.getAttribute('data-submittable')).toBe('true');
|
|
112
|
+
expect(textField.getAttribute('data-submittable-type')).toBe('add');
|
|
113
|
+
expect(textField.getAttribute('data-placeholder')).toBe('+');
|
|
114
|
+
expect(textField.getAttribute('data-value')).toBe('');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('passes assetTags from store as suggestions', () => {
|
|
118
|
+
const suggestions = ['x', 'y', 'z'];
|
|
119
|
+
const { getByTestId } = renderComponent([], suggestions);
|
|
120
|
+
const textField = getByTestId('mock-text-field');
|
|
121
|
+
|
|
122
|
+
const suggestionsAttr = textField.getAttribute('data-suggestions');
|
|
123
|
+
expect(suggestionsAttr).toBeTruthy();
|
|
124
|
+
expect(JSON.parse(suggestionsAttr)).toEqual(suggestions);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('dispatches addAssetTag when submitting a new tag', () => {
|
|
128
|
+
renderComponent(['existing']);
|
|
129
|
+
|
|
130
|
+
capturedDoSubmit('brand-new');
|
|
131
|
+
|
|
132
|
+
expect(addAssetTag).toHaveBeenCalledWith(MOCK_API, MOCK_ASSET_ID, 'brand-new');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('does not dispatch addAssetTag when submitting a duplicate tag', () => {
|
|
136
|
+
vi.mocked(addAssetTag).mockClear();
|
|
137
|
+
renderComponent(['already-here']);
|
|
138
|
+
|
|
139
|
+
capturedDoSubmit('already-here');
|
|
140
|
+
|
|
141
|
+
expect(addAssetTag).not.toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('dispatches deleteAssetTag when removing a tag', () => {
|
|
145
|
+
renderComponent(['removable']);
|
|
146
|
+
|
|
147
|
+
capturedRemoveHandlers['removable']();
|
|
148
|
+
|
|
149
|
+
expect(deleteAssetTag).toHaveBeenCalledWith(MOCK_API, MOCK_ASSET_ID, 'removable');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('renders with no tags', () => {
|
|
153
|
+
const { container, getByTestId } = renderComponent([]);
|
|
154
|
+
const renderedTags = container.querySelectorAll('.mock-tag');
|
|
155
|
+
|
|
156
|
+
expect(renderedTags).toHaveLength(0);
|
|
157
|
+
expect(getByTestId('mock-text-field')).toBeTruthy();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from '@testing-library/react';
|
|
4
|
+
|
|
5
|
+
const MOCK_FORMATTED_DATE = '4/9/2026 3:00 PM';
|
|
6
|
+
|
|
7
|
+
vi.mock('../../Util', async (importOriginal) => {
|
|
8
|
+
const actual = (await importOriginal()) as any;
|
|
9
|
+
return {
|
|
10
|
+
...actual,
|
|
11
|
+
formatDateDefault: vi.fn(() => MOCK_FORMATTED_DATE)
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
import AssetsTableDateCellMemoized from './AssetsTableDateCell';
|
|
16
|
+
import { formatDateDefault } from '../../Util';
|
|
17
|
+
|
|
18
|
+
const MOCK_UNIX_TIMESTAMP = 1776013200;
|
|
19
|
+
|
|
20
|
+
const buildCellProp = (values: Record<string, any>) => ({
|
|
21
|
+
row: { values }
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('AssetsTableDateCell', () => {
|
|
25
|
+
it('renders date_created when date_created prop is true and value exists', () => {
|
|
26
|
+
const cell = buildCellProp({ date_created: MOCK_UNIX_TIMESTAMP });
|
|
27
|
+
const { container } = render(
|
|
28
|
+
<AssetsTableDateCellMemoized
|
|
29
|
+
cell={cell}
|
|
30
|
+
date_created={true}
|
|
31
|
+
/>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(formatDateDefault).toHaveBeenCalledWith(MOCK_UNIX_TIMESTAMP);
|
|
35
|
+
expect(container.textContent).toBe(MOCK_FORMATTED_DATE);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('renders date_modified when date_created is false', () => {
|
|
39
|
+
const cell = buildCellProp({ date_modified: MOCK_UNIX_TIMESTAMP });
|
|
40
|
+
const { container } = render(
|
|
41
|
+
<AssetsTableDateCellMemoized
|
|
42
|
+
cell={cell}
|
|
43
|
+
date_created={false}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(formatDateDefault).toHaveBeenCalledWith(MOCK_UNIX_TIMESTAMP);
|
|
48
|
+
expect(container.textContent).toBe(MOCK_FORMATTED_DATE);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('falls through to date_updated when date_modified is absent', () => {
|
|
52
|
+
const cell = buildCellProp({ date_updated: MOCK_UNIX_TIMESTAMP });
|
|
53
|
+
const { container } = render(
|
|
54
|
+
<AssetsTableDateCellMemoized
|
|
55
|
+
cell={cell}
|
|
56
|
+
date_created={false}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
expect(formatDateDefault).toHaveBeenCalledWith(MOCK_UNIX_TIMESTAMP);
|
|
61
|
+
expect(container.textContent).toBe(MOCK_FORMATTED_DATE);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('prefers date_modified over date_updated when both exist', () => {
|
|
65
|
+
const modifiedTimestamp = 1776099600;
|
|
66
|
+
const cell = buildCellProp({
|
|
67
|
+
date_modified: modifiedTimestamp,
|
|
68
|
+
date_updated: MOCK_UNIX_TIMESTAMP
|
|
69
|
+
});
|
|
70
|
+
const { container } = render(
|
|
71
|
+
<AssetsTableDateCellMemoized
|
|
72
|
+
cell={cell}
|
|
73
|
+
date_created={false}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(formatDateDefault).toHaveBeenCalledWith(modifiedTimestamp);
|
|
78
|
+
expect(container.textContent).toBe(MOCK_FORMATTED_DATE);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('skips date_created value when date_created prop is false', () => {
|
|
82
|
+
vi.mocked(formatDateDefault).mockClear();
|
|
83
|
+
const cell = buildCellProp({ date_created: MOCK_UNIX_TIMESTAMP });
|
|
84
|
+
const { container } = render(
|
|
85
|
+
<AssetsTableDateCellMemoized
|
|
86
|
+
cell={cell}
|
|
87
|
+
date_created={false}
|
|
88
|
+
/>
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
expect(formatDateDefault).not.toHaveBeenCalled();
|
|
92
|
+
expect(container.innerHTML).toBe('');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('returns null when no date values are present', () => {
|
|
96
|
+
const cell = buildCellProp({});
|
|
97
|
+
const { container } = render(
|
|
98
|
+
<AssetsTableDateCellMemoized
|
|
99
|
+
cell={cell}
|
|
100
|
+
date_created={false}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
expect(container.innerHTML).toBe('');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { assets as copy } from '../../constants/copy';
|
|
3
|
-
import Tag from '../
|
|
3
|
+
import Tag from '../tag/Tag';
|
|
4
4
|
import TextField from '../text-field/TextField';
|
|
5
5
|
import { addAssetTag, deleteAssetTag, deleteAssetTags } from '../../redux/actions/asset-list';
|
|
6
6
|
import { connect } from 'react-redux';
|
|
@@ -15,7 +15,7 @@ import SelectField from '../select-field/SelectField';
|
|
|
15
15
|
import { DELIMITER_OPTIONS } from '../../constants/assets';
|
|
16
16
|
import TextField from '../text-field/TextField';
|
|
17
17
|
import AssetsTableTagsPivot from './AssetsTableTagsPivot';
|
|
18
|
-
import Tag from '../
|
|
18
|
+
import Tag from '../tag/Tag';
|
|
19
19
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
20
20
|
import { faEraser } from '@fortawesome/free-solid-svg-icons';
|
|
21
21
|
import { ICON_FILE_ADD, ICON_FILE_REPLACE, ICON_TIMES, ICON_UPLOAD } from '../../constants/icons';
|
|
@@ -1,6 +1,54 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { IChangeReportNode, IChangeReportSection } from '../../constants/snippets';
|
|
3
3
|
import * as copy from '../../constants/copy';
|
|
4
|
+
import {
|
|
5
|
+
ICON_IMAGE,
|
|
6
|
+
ICON_VIDEO,
|
|
7
|
+
ICON_LAYER_GROUP,
|
|
8
|
+
ICON_CODE,
|
|
9
|
+
ICON_FILE_ARCHIVE,
|
|
10
|
+
ICON_IMAGE_SEQ_TYPE,
|
|
11
|
+
ICON_FONT,
|
|
12
|
+
ICON_AUDIO_TYPE,
|
|
13
|
+
ICON_HASHTAG,
|
|
14
|
+
ICON_TOGGLE_ON,
|
|
15
|
+
ICON_BARS,
|
|
16
|
+
ICON_PALETTE,
|
|
17
|
+
ICON_QRCODE
|
|
18
|
+
} from '../../constants/icons';
|
|
19
|
+
|
|
20
|
+
const TYPE_ICONS: Record<string, React.ReactNode> = {
|
|
21
|
+
// Layer types
|
|
22
|
+
image: ICON_IMAGE,
|
|
23
|
+
video: ICON_VIDEO,
|
|
24
|
+
video_composition: ICON_LAYER_GROUP,
|
|
25
|
+
html: ICON_CODE,
|
|
26
|
+
template: ICON_FILE_ARCHIVE,
|
|
27
|
+
image_sequence: ICON_IMAGE_SEQ_TYPE,
|
|
28
|
+
text: ICON_FONT,
|
|
29
|
+
audio: ICON_AUDIO_TYPE,
|
|
30
|
+
solid: ICON_IMAGE,
|
|
31
|
+
after_effects: ICON_VIDEO,
|
|
32
|
+
// Variable types (matches getVariableIcon in Story Editor)
|
|
33
|
+
number: ICON_HASHTAG,
|
|
34
|
+
boolean: ICON_TOGGLE_ON,
|
|
35
|
+
enum: ICON_BARS,
|
|
36
|
+
color: ICON_PALETTE,
|
|
37
|
+
qr_code: ICON_QRCODE
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const TYPE_COLORS: Record<string, string> = {
|
|
41
|
+
video: 'rgba(236, 22, 150, 1)',
|
|
42
|
+
image: 'rgba(241, 191, 21, 1)',
|
|
43
|
+
audio: 'rgba(72, 177, 222, 1)',
|
|
44
|
+
template: 'rgba(208, 80, 35, 1)',
|
|
45
|
+
video_composition: 'rgba(215, 22, 22, 1)',
|
|
46
|
+
text: 'rgba(71, 212, 125, 1)',
|
|
47
|
+
image_sequence: 'rgba(241, 191, 21, 1)',
|
|
48
|
+
html: '#ae6cff',
|
|
49
|
+
solid: '#AAAAAA',
|
|
50
|
+
after_effects: 'rgba(236, 22, 150, 1)'
|
|
51
|
+
};
|
|
4
52
|
|
|
5
53
|
interface IChangeReportTreeProps {
|
|
6
54
|
sections: IChangeReportSection[] | null;
|
|
@@ -15,6 +63,33 @@ interface IChangeReportNodeProps {
|
|
|
15
63
|
depth: number;
|
|
16
64
|
}
|
|
17
65
|
|
|
66
|
+
const isColorValue = (val: string): boolean => {
|
|
67
|
+
if (!val) return false;
|
|
68
|
+
return /^#[0-9a-fA-F]{3,8}$/.test(val) || /^rgba?\(/.test(val);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const ValueDisplay: React.FC<{ value: string }> = ({ value }) => {
|
|
72
|
+
if (value === '*default*' || value === '*empty*') {
|
|
73
|
+
// Check if this could be a transparent/default color
|
|
74
|
+
return <>{value}</>;
|
|
75
|
+
}
|
|
76
|
+
if (isColorValue(value)) {
|
|
77
|
+
return (
|
|
78
|
+
<>
|
|
79
|
+
<span className='change-report-color-chip-wrapper'>
|
|
80
|
+
<span className='change-report-color-chip-checkerboard' />
|
|
81
|
+
<span
|
|
82
|
+
className='change-report-color-chip-color'
|
|
83
|
+
style={{ backgroundColor: value }}
|
|
84
|
+
/>
|
|
85
|
+
</span>
|
|
86
|
+
{value}
|
|
87
|
+
</>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return <>{value}</>;
|
|
91
|
+
};
|
|
92
|
+
|
|
18
93
|
const STATUS_LABELS: Record<IChangeReportNode['status'], string> = {
|
|
19
94
|
added: copy.publish.statusAdded,
|
|
20
95
|
removed: copy.publish.statusRemoved,
|
|
@@ -25,16 +100,12 @@ const ChangeReportNode: React.FC<IChangeReportNodeProps> = ({ node, defaultExpan
|
|
|
25
100
|
const [expanded, setExpanded] = React.useState(defaultExpanded);
|
|
26
101
|
|
|
27
102
|
const hasChildren = node.children && node.children.length > 0;
|
|
28
|
-
const
|
|
103
|
+
const hasOldValue = node.oldValue !== undefined && node.oldValue !== null;
|
|
104
|
+
const hasNewValue = node.newValue !== undefined && node.newValue !== null;
|
|
105
|
+
const hasValues = hasOldValue || hasNewValue;
|
|
29
106
|
|
|
30
|
-
const oldDisplay =
|
|
31
|
-
|
|
32
|
-
? '*empty*'
|
|
33
|
-
: node.oldValue;
|
|
34
|
-
const newDisplay =
|
|
35
|
-
node.newValue === null || node.newValue === '' || node.newValue === undefined
|
|
36
|
-
? '*empty*'
|
|
37
|
-
: node.newValue;
|
|
107
|
+
const oldDisplay = !hasOldValue || node.oldValue === '' ? '*empty*' : node.oldValue;
|
|
108
|
+
const newDisplay = !hasNewValue || node.newValue === '' ? '*empty*' : node.newValue;
|
|
38
109
|
|
|
39
110
|
return (
|
|
40
111
|
<div
|
|
@@ -54,16 +125,33 @@ const ChangeReportNode: React.FC<IChangeReportNodeProps> = ({ node, defaultExpan
|
|
|
54
125
|
<span className={`change-report-status change-report-status--${node.status}`}>
|
|
55
126
|
{STATUS_LABELS[node.status]}
|
|
56
127
|
</span>
|
|
128
|
+
{(node.layerType || node.varType) && TYPE_ICONS[node.layerType || node.varType] && (
|
|
129
|
+
<span
|
|
130
|
+
className='change-report-layer-icon'
|
|
131
|
+
style={
|
|
132
|
+
TYPE_COLORS[node.layerType || node.varType]
|
|
133
|
+
? { color: TYPE_COLORS[node.layerType || node.varType] }
|
|
134
|
+
: undefined
|
|
135
|
+
}>
|
|
136
|
+
{TYPE_ICONS[node.layerType || node.varType]}
|
|
137
|
+
</span>
|
|
138
|
+
)}
|
|
57
139
|
<span className='change-report-label'>{node.label}</span>
|
|
58
140
|
{hasValues && (
|
|
59
141
|
<span className='change-report-values'>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
142
|
+
{hasOldValue && (
|
|
143
|
+
<span className='change-report-value change-report-value--old'>
|
|
144
|
+
<ValueDisplay value={oldDisplay} />
|
|
145
|
+
</span>
|
|
146
|
+
)}
|
|
147
|
+
{hasOldValue && hasNewValue && (
|
|
148
|
+
<span className='change-report-value-arrow'>→</span>
|
|
149
|
+
)}
|
|
150
|
+
{hasNewValue && (
|
|
151
|
+
<span className='change-report-value change-report-value--new'>
|
|
152
|
+
<ValueDisplay value={newDisplay} />
|
|
153
|
+
</span>
|
|
154
|
+
)}
|
|
67
155
|
</span>
|
|
68
156
|
)}
|
|
69
157
|
</div>
|