box-ui-elements 23.4.0-beta.30 → 23.4.0-beta.32
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/explorer.css +1 -1
- package/dist/explorer.js +1 -1
- package/dist/openwith.js +1 -1
- package/dist/picker.js +1 -1
- package/dist/preview.css +1 -1
- package/dist/preview.js +1 -1
- package/dist/sharing.js +1 -1
- package/dist/sidebar.css +1 -1
- package/dist/sidebar.js +1 -1
- package/dist/uploader.js +1 -1
- package/es/common/types/metadata.js.flow +6 -0
- package/es/common/types/metadata.js.map +1 -1
- package/es/elements/content-sidebar/SidebarNav.js +14 -3
- package/es/elements/content-sidebar/SidebarNav.js.flow +23 -3
- package/es/elements/content-sidebar/SidebarNav.js.map +1 -1
- package/es/elements/content-sidebar/SidebarNavTablist.js +58 -17
- package/es/elements/content-sidebar/SidebarNavTablist.js.flow +80 -21
- package/es/elements/content-sidebar/SidebarNavTablist.js.map +1 -1
- package/es/features/metadata-instance-editor/CascadePolicy.js +53 -23
- package/es/features/metadata-instance-editor/CascadePolicy.js.flow +69 -27
- package/es/features/metadata-instance-editor/CascadePolicy.js.map +1 -1
- package/es/features/metadata-instance-editor/Instance.js +26 -4
- package/es/features/metadata-instance-editor/Instance.js.flow +33 -4
- package/es/features/metadata-instance-editor/Instance.js.map +1 -1
- package/es/features/metadata-instance-editor/constants.js +4 -1
- package/es/features/metadata-instance-editor/constants.js.flow +10 -1
- package/es/features/metadata-instance-editor/constants.js.map +1 -1
- package/es/features/metadata-instance-editor/messages.js +16 -0
- package/es/features/metadata-instance-editor/messages.js.flow +21 -0
- package/es/features/metadata-instance-editor/messages.js.map +1 -1
- package/es/features/metadata-instance-editor/stories/tests/CascadePolicy-visual.stories.js +32 -0
- package/es/features/metadata-instance-editor/stories/tests/CascadePolicy-visual.stories.js.flow +36 -0
- package/es/features/metadata-instance-editor/stories/tests/CascadePolicy-visual.stories.js.map +1 -0
- package/i18n/bn-IN.js +4 -0
- package/i18n/da-DK.js +4 -0
- package/i18n/de-DE.js +4 -0
- package/i18n/en-AU.js +4 -0
- package/i18n/en-CA.js +4 -0
- package/i18n/en-GB.js +4 -0
- package/i18n/en-US.js +4 -0
- package/i18n/en-US.properties +8 -0
- package/i18n/en-x-pseudo.js +4 -0
- package/i18n/es-419.js +4 -0
- package/i18n/es-ES.js +4 -0
- package/i18n/fi-FI.js +4 -0
- package/i18n/fr-CA.js +4 -0
- package/i18n/fr-FR.js +4 -0
- package/i18n/hi-IN.js +4 -0
- package/i18n/it-IT.js +4 -0
- package/i18n/ja-JP.js +4 -0
- package/i18n/ko-KR.js +4 -0
- package/i18n/nb-NO.js +4 -0
- package/i18n/nl-NL.js +4 -0
- package/i18n/pl-PL.js +4 -0
- package/i18n/pt-BR.js +4 -0
- package/i18n/ru-RU.js +4 -0
- package/i18n/sv-SE.js +4 -0
- package/i18n/tr-TR.js +4 -0
- package/i18n/zh-CN.js +4 -0
- package/i18n/zh-TW.js +4 -0
- package/package.json +7 -7
- package/src/common/types/metadata.js +6 -0
- package/src/elements/content-sidebar/SidebarNav.js +23 -3
- package/src/elements/content-sidebar/SidebarNavTablist.js +80 -21
- package/src/elements/content-sidebar/__tests__/SidebarNav.test.js +99 -147
- package/src/elements/content-sidebar/__tests__/SidebarNavTablist.test.js +189 -42
- package/src/features/metadata-instance-editor/CascadePolicy.js +69 -27
- package/src/features/metadata-instance-editor/Instance.js +33 -4
- package/src/features/metadata-instance-editor/__tests__/CascadePolicy.test.js +70 -63
- package/src/features/metadata-instance-editor/__tests__/Instance.test.js +34 -19
- package/src/features/metadata-instance-editor/__tests__/Instances.test.js +15 -10
- package/src/features/metadata-instance-editor/__tests__/MetadataInstanceEditor.test.js +53 -10
- package/src/features/metadata-instance-editor/__tests__/__snapshots__/Instance.test.js.snap +2 -1
- package/src/features/metadata-instance-editor/constants.js +10 -1
- package/src/features/metadata-instance-editor/messages.js +21 -0
- package/src/features/metadata-instance-editor/stories/tests/CascadePolicy-visual.stories.js +36 -0
- package/src/features/metadata-instance-editor/__tests__/__snapshots__/CascadePolicy.test.js.snap +0 -108
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { FormattedMessage } from 'react-intl';
|
|
4
3
|
|
|
5
|
-
import {
|
|
4
|
+
import { InlineNotice } from '@box/blueprint-web';
|
|
5
|
+
import { useCallback } from 'react';
|
|
6
|
+
import { FormattedMessage, useIntl } from 'react-intl';
|
|
7
|
+
|
|
8
|
+
// $FlowFixMe
|
|
9
|
+
import { BoxAiAdvancedColor, BoxAiColor } from '@box/blueprint-web-assets/icons/Medium';
|
|
10
|
+
import { type AgentType } from '@box/box-ai-agent-selector';
|
|
11
|
+
|
|
6
12
|
// $FlowFixMe
|
|
7
|
-
import
|
|
13
|
+
import { BoxAiAgentSelectorWithApiContainer } from '@box/box-ai-agent-selector';
|
|
8
14
|
|
|
9
15
|
import Toggle from '../../components/toggle';
|
|
10
16
|
import { RadioButton, RadioGroup } from '../../components/radio';
|
|
@@ -12,23 +18,11 @@ import Link from '../../components/link/Link';
|
|
|
12
18
|
import IconAlertDefault from '../../icons/general/IconAlertDefault';
|
|
13
19
|
import messages from './messages';
|
|
14
20
|
import './CascadePolicy.scss';
|
|
21
|
+
import { STANDARD_AGENT_ID, ENHANCED_AGENT_ID } from './constants';
|
|
15
22
|
|
|
16
23
|
const COMMUNITY_LINK = 'https://support.box.com/hc/en-us/articles/360044195873-Cascading-metadata-in-folders';
|
|
17
24
|
const AI_LINK = 'https://www.box.com/ai';
|
|
18
25
|
|
|
19
|
-
const agents = [
|
|
20
|
-
{
|
|
21
|
-
id: '1',
|
|
22
|
-
name: 'Basic',
|
|
23
|
-
isEnterpriseDefault: true,
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
id: '2',
|
|
27
|
-
name: 'Enhanced (Gemini 2.5 Pro)',
|
|
28
|
-
isEnterpriseDefault: false,
|
|
29
|
-
},
|
|
30
|
-
];
|
|
31
|
-
|
|
32
26
|
type Props = {
|
|
33
27
|
canEdit: boolean,
|
|
34
28
|
canUseAIFolderExtraction: boolean,
|
|
@@ -37,8 +31,9 @@ type Props = {
|
|
|
37
31
|
isCascadingEnabled: boolean,
|
|
38
32
|
isCascadingOverwritten: boolean,
|
|
39
33
|
isCustomMetadata: boolean,
|
|
40
|
-
|
|
34
|
+
isExistingCascadePolicy: boolean,
|
|
41
35
|
onAIFolderExtractionToggle: (value: boolean) => void,
|
|
36
|
+
onAIAgentSelect?: (agent: AgentType | null) => void,
|
|
42
37
|
onCascadeModeChange: (value: boolean) => void,
|
|
43
38
|
onCascadeToggle: (value: boolean) => void,
|
|
44
39
|
shouldShowCascadeOptions: boolean,
|
|
@@ -52,26 +47,71 @@ const CascadePolicy = ({
|
|
|
52
47
|
isCascadingOverwritten,
|
|
53
48
|
isCustomMetadata,
|
|
54
49
|
isAIFolderExtractionEnabled,
|
|
55
|
-
|
|
50
|
+
isExistingCascadePolicy,
|
|
56
51
|
onAIFolderExtractionToggle,
|
|
52
|
+
onAIAgentSelect,
|
|
57
53
|
onCascadeToggle,
|
|
58
54
|
onCascadeModeChange,
|
|
59
55
|
shouldShowCascadeOptions,
|
|
60
56
|
}: Props) => {
|
|
57
|
+
const { formatMessage } = useIntl();
|
|
58
|
+
|
|
61
59
|
const readOnlyState = isCascadingEnabled ? (
|
|
62
60
|
<div className="metadata-cascade-notice">
|
|
63
61
|
<FormattedMessage {...messages.metadataCascadePolicyEnabledInfo} />
|
|
64
62
|
</div>
|
|
65
63
|
) : null;
|
|
66
64
|
|
|
65
|
+
const agents = React.useMemo(
|
|
66
|
+
() => [
|
|
67
|
+
{
|
|
68
|
+
id: STANDARD_AGENT_ID,
|
|
69
|
+
name: formatMessage(messages.standardAgentName),
|
|
70
|
+
isEnterpriseDefault: true,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: ENHANCED_AGENT_ID,
|
|
74
|
+
name: formatMessage(messages.enhancedAgentName),
|
|
75
|
+
isEnterpriseDefault: false,
|
|
76
|
+
customIcon: BoxAiAdvancedColor,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
[formatMessage],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// BoxAiAgentSelectorWithApiContainer expects a function that returns a Promise<AgentListResponse>
|
|
83
|
+
// Since we're passing in our own agents, we don't need to make an API call,
|
|
84
|
+
// so we wrap the store data in a Promise to satisfy the component's interface requirements.
|
|
85
|
+
const agentFetcher = useCallback(() => {
|
|
86
|
+
return Promise.resolve({ agents });
|
|
87
|
+
}, [agents]);
|
|
88
|
+
|
|
89
|
+
const handleAgentSelect = useCallback(
|
|
90
|
+
(agent: AgentType | null) => {
|
|
91
|
+
if (onAIAgentSelect) {
|
|
92
|
+
onAIAgentSelect(agent);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
[onAIAgentSelect],
|
|
96
|
+
);
|
|
97
|
+
|
|
67
98
|
return canEdit ? (
|
|
68
99
|
<>
|
|
100
|
+
{isExistingCascadePolicy && (
|
|
101
|
+
<InlineNotice
|
|
102
|
+
variant="info"
|
|
103
|
+
variantIconAriaLabel={formatMessage(messages.cascadePolicyOptionsDisabledNoticeIconAriaLabel)}
|
|
104
|
+
>
|
|
105
|
+
<FormattedMessage {...messages.cascadePolicyOptionsDisabledNotice} />
|
|
106
|
+
</InlineNotice>
|
|
107
|
+
)}
|
|
69
108
|
<div className="metadata-cascade-editor">
|
|
70
109
|
<div className="metadata-cascade-enable" data-testid="metadata-cascade-enable">
|
|
71
110
|
<div>
|
|
72
111
|
<FormattedMessage tagName="strong" {...messages.enableCascadePolicy} />
|
|
73
112
|
{!isCustomMetadata && (
|
|
74
113
|
<Toggle
|
|
114
|
+
aria-label={formatMessage(messages.enableCascadePolicy)}
|
|
75
115
|
className={`metadata-cascade-toggle ${
|
|
76
116
|
isCascadingEnabled ? 'cascade-on' : 'cascade-off'
|
|
77
117
|
}`}
|
|
@@ -113,10 +153,12 @@ const CascadePolicy = ({
|
|
|
113
153
|
value={isCascadingOverwritten ? 'overwrite' : 'skip'}
|
|
114
154
|
>
|
|
115
155
|
<RadioButton
|
|
156
|
+
isDisabled={isExistingCascadePolicy}
|
|
116
157
|
label={<FormattedMessage {...messages.cascadePolicySkipMode} />}
|
|
117
158
|
value="skip"
|
|
118
159
|
/>
|
|
119
160
|
<RadioButton
|
|
161
|
+
isDisabled={isExistingCascadePolicy}
|
|
120
162
|
label={<FormattedMessage {...messages.cascadePolicyOverwriteMode} />}
|
|
121
163
|
value="overwrite"
|
|
122
164
|
/>
|
|
@@ -128,12 +170,13 @@ const CascadePolicy = ({
|
|
|
128
170
|
<div className="metadata-cascade-editor" data-testid="ai-folder-extraction">
|
|
129
171
|
<div className="metadata-cascade-enable">
|
|
130
172
|
<div>
|
|
131
|
-
<
|
|
173
|
+
<BoxAiColor className="metadata-cascade-ai-logo" width={16} height={16} />
|
|
132
174
|
<FormattedMessage tagName="strong" {...messages.enableAIAutofill} />
|
|
133
175
|
<Toggle
|
|
176
|
+
aria-label={formatMessage(messages.enableAIAutofill)}
|
|
134
177
|
className="metadata-cascade-toggle"
|
|
135
178
|
isOn={isAIFolderExtractionEnabled}
|
|
136
|
-
isDisabled={
|
|
179
|
+
isDisabled={isExistingCascadePolicy}
|
|
137
180
|
label=""
|
|
138
181
|
onChange={e => onAIFolderExtractionToggle(e.target.checked)}
|
|
139
182
|
/>
|
|
@@ -145,14 +188,13 @@ const CascadePolicy = ({
|
|
|
145
188
|
<FormattedMessage {...messages.aiAutofillLearnMore} />
|
|
146
189
|
</Link>
|
|
147
190
|
</div>
|
|
148
|
-
{canUseAIFolderExtractionAgentSelector && (
|
|
191
|
+
{canUseAIFolderExtractionAgentSelector && isAIFolderExtractionEnabled && (
|
|
149
192
|
<div className="metadata-cascade-ai-agent-selector">
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
variant="sidebar"
|
|
193
|
+
<BoxAiAgentSelectorWithApiContainer
|
|
194
|
+
disabled={isExistingCascadePolicy}
|
|
195
|
+
fetcher={agentFetcher}
|
|
196
|
+
onSelectAgent={handleAgentSelect}
|
|
197
|
+
recordAction={() => {}}
|
|
156
198
|
/>
|
|
157
199
|
</div>
|
|
158
200
|
)}
|
|
@@ -6,6 +6,7 @@ import isEqual from 'lodash/isEqual';
|
|
|
6
6
|
import cloneDeep from 'lodash/cloneDeep';
|
|
7
7
|
import noop from 'lodash/noop';
|
|
8
8
|
|
|
9
|
+
import type { AgentType } from '@box/box-ai-agent-selector';
|
|
9
10
|
import Collapsible from '../../components/collapsible/Collapsible';
|
|
10
11
|
import Form from '../../components/form-elements/form/Form';
|
|
11
12
|
import LoadingIndicatorWrapper from '../../components/loading-indicator/LoadingIndicatorWrapper';
|
|
@@ -24,7 +25,12 @@ import MetadataInstanceConfirmDialog from './MetadataInstanceConfirmDialog';
|
|
|
24
25
|
import Footer from './Footer';
|
|
25
26
|
import messages from './messages';
|
|
26
27
|
import { FIELD_TYPE_FLOAT, FIELD_TYPE_INTEGER } from '../metadata-instance-fields/constants';
|
|
27
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
CASCADE_POLICY_TYPE_AI_EXTRACT,
|
|
30
|
+
TEMPLATE_CUSTOM_PROPERTIES,
|
|
31
|
+
ENHANCED_AGENT_CONFIGURATION,
|
|
32
|
+
ENHANCED_AGENT_ID,
|
|
33
|
+
} from './constants';
|
|
28
34
|
import {
|
|
29
35
|
JSON_PATCH_OP_REMOVE,
|
|
30
36
|
JSON_PATCH_OP_ADD,
|
|
@@ -38,6 +44,7 @@ import type {
|
|
|
38
44
|
MetadataFields,
|
|
39
45
|
MetadataTemplate,
|
|
40
46
|
MetadataCascadePolicy,
|
|
47
|
+
MetadataCascadePolicyConfiguration,
|
|
41
48
|
MetadataCascadingPolicyData,
|
|
42
49
|
MetadataTemplateField,
|
|
43
50
|
MetadataFieldValue,
|
|
@@ -69,6 +76,7 @@ type Props = {
|
|
|
69
76
|
};
|
|
70
77
|
|
|
71
78
|
type State = {
|
|
79
|
+
cascadePolicyConfiguration: MetadataCascadePolicyConfiguration | null,
|
|
72
80
|
data: Object,
|
|
73
81
|
errors: { [string]: React.Node },
|
|
74
82
|
isAIFolderExtractionEnabled: boolean,
|
|
@@ -212,6 +220,7 @@ class Instance extends React.PureComponent<Props, State> {
|
|
|
212
220
|
onSave,
|
|
213
221
|
}: Props = this.props;
|
|
214
222
|
const {
|
|
223
|
+
cascadePolicyConfiguration,
|
|
215
224
|
data: currentData,
|
|
216
225
|
errors,
|
|
217
226
|
isAIFolderExtractionEnabled,
|
|
@@ -229,6 +238,7 @@ class Instance extends React.PureComponent<Props, State> {
|
|
|
229
238
|
// reset state if cascading policy is removed
|
|
230
239
|
isAIFolderExtractionEnabled: isCascadingEnabled ? isAIFolderExtractionEnabled : false,
|
|
231
240
|
});
|
|
241
|
+
|
|
232
242
|
onSave(
|
|
233
243
|
id,
|
|
234
244
|
this.createJSONPatch(currentData, originalData),
|
|
@@ -239,6 +249,7 @@ class Instance extends React.PureComponent<Props, State> {
|
|
|
239
249
|
isEnabled: isCascadingEnabled,
|
|
240
250
|
overwrite: isCascadingOverwritten,
|
|
241
251
|
isAIFolderExtractionEnabled,
|
|
252
|
+
cascadePolicyConfiguration,
|
|
242
253
|
}
|
|
243
254
|
: undefined,
|
|
244
255
|
cloneDeep(currentData),
|
|
@@ -342,6 +353,23 @@ class Instance extends React.PureComponent<Props, State> {
|
|
|
342
353
|
this.setState({ isAIFolderExtractionEnabled: value }, this.setDirty);
|
|
343
354
|
};
|
|
344
355
|
|
|
356
|
+
/**
|
|
357
|
+
* Handles the selection of an AI agent
|
|
358
|
+
* @param {AgentType | null} agent - The selected agent
|
|
359
|
+
*/
|
|
360
|
+
onAIAgentSelect = (agent: AgentType | null): void => {
|
|
361
|
+
// '2' is the id for the enhanced agent
|
|
362
|
+
if (agent && agent.id === ENHANCED_AGENT_ID) {
|
|
363
|
+
this.setState({
|
|
364
|
+
cascadePolicyConfiguration: {
|
|
365
|
+
agent: ENHANCED_AGENT_CONFIGURATION,
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
} else {
|
|
369
|
+
this.setState({ cascadePolicyConfiguration: null });
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
|
|
345
373
|
/**
|
|
346
374
|
* Returns the state from props
|
|
347
375
|
*
|
|
@@ -351,6 +379,7 @@ class Instance extends React.PureComponent<Props, State> {
|
|
|
351
379
|
const isCascadingEnabled = this.isCascadingEnabledThroughProps(props);
|
|
352
380
|
|
|
353
381
|
return {
|
|
382
|
+
cascadePolicyConfiguration: null,
|
|
354
383
|
data: cloneDeep(props.data),
|
|
355
384
|
errors: {},
|
|
356
385
|
isAIFolderExtractionEnabled: this.isAIFolderExtractionEnabledThroughProps(props),
|
|
@@ -641,8 +670,7 @@ class Instance extends React.PureComponent<Props, State> {
|
|
|
641
670
|
// Animate short and tall cards at consistent speeds.
|
|
642
671
|
const animationDuration = (fields.length + 1) * 50;
|
|
643
672
|
|
|
644
|
-
const
|
|
645
|
-
this.isCascadingEnabledThroughProps(this.props) && this.isAIFolderExtractionEnabledThroughProps(this.props);
|
|
673
|
+
const isExistingCascadePolicy = this.isCascadingEnabledThroughProps(this.props);
|
|
646
674
|
|
|
647
675
|
return (
|
|
648
676
|
<div ref={this.collapsibleRef}>
|
|
@@ -681,7 +709,8 @@ class Instance extends React.PureComponent<Props, State> {
|
|
|
681
709
|
isCascadingEnabled={isCascadingEnabled}
|
|
682
710
|
isCascadingOverwritten={isCascadingOverwritten}
|
|
683
711
|
isCustomMetadata={isProperties}
|
|
684
|
-
|
|
712
|
+
isExistingCascadePolicy={isExistingCascadePolicy}
|
|
713
|
+
onAIAgentSelect={this.onAIAgentSelect}
|
|
685
714
|
onAIFolderExtractionToggle={this.onAIFolderExtractionToggle}
|
|
686
715
|
onCascadeModeChange={this.onCascadeModeChange}
|
|
687
716
|
onCascadeToggle={this.onCascadeToggle}
|
|
@@ -13,49 +13,66 @@ describe('features/metadata-instance-editor/CascadePolicy', () => {
|
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
test('should correctly render cascade policy read only mode', () => {
|
|
16
|
-
|
|
17
|
-
expect(
|
|
16
|
+
render(<CascadePolicy isCascadingEnabled shouldShowCascadeOptions canEdit={false} />);
|
|
17
|
+
expect(
|
|
18
|
+
screen.getByText(
|
|
19
|
+
'This template and its values are being cascaded to all items in this folder and its subfolders.',
|
|
20
|
+
),
|
|
21
|
+
).toBeInTheDocument();
|
|
18
22
|
});
|
|
23
|
+
|
|
19
24
|
test('should correctly render cascade policy in edit mode', () => {
|
|
20
|
-
|
|
25
|
+
render(
|
|
21
26
|
<CascadePolicy
|
|
22
|
-
|
|
27
|
+
canEdit
|
|
23
28
|
isCascadingEnabled
|
|
24
|
-
isEditable
|
|
25
29
|
onCascadeModeChange={jest.fn()}
|
|
26
30
|
onCascadeToggle={jest.fn()}
|
|
27
31
|
shouldShowCascadeOptions
|
|
28
32
|
/>,
|
|
29
33
|
);
|
|
30
|
-
expect(
|
|
34
|
+
expect(screen.getByTestId('metadata-cascade-enable')).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByText('Enable Cascade Policy')).toBeInTheDocument();
|
|
31
36
|
});
|
|
37
|
+
|
|
32
38
|
test('should correctly render cascade policy in edit mode and overwrite is on', () => {
|
|
33
|
-
|
|
39
|
+
render(
|
|
34
40
|
<CascadePolicy
|
|
35
|
-
|
|
41
|
+
canEdit
|
|
36
42
|
isCascadingEnabled
|
|
37
|
-
|
|
43
|
+
isCascadingOverwritten
|
|
38
44
|
onCascadeModeChange={jest.fn()}
|
|
39
45
|
onCascadeToggle={jest.fn()}
|
|
40
|
-
shouldCascadeOverwrite
|
|
41
46
|
shouldShowCascadeOptions
|
|
42
47
|
/>,
|
|
43
48
|
);
|
|
44
|
-
expect(
|
|
49
|
+
expect(screen.getByTestId('metadata-cascade-enable')).toBeInTheDocument();
|
|
50
|
+
expect(screen.getByText('Enable Cascade Policy')).toBeInTheDocument();
|
|
51
|
+
expect(screen.getByLabelText('Overwrite all existing template values')).toBeInTheDocument();
|
|
45
52
|
});
|
|
53
|
+
|
|
46
54
|
test('should correctly render cascade policy when the template is Custom Metadata', () => {
|
|
47
|
-
|
|
55
|
+
render(
|
|
48
56
|
<CascadePolicy
|
|
49
57
|
canEdit
|
|
50
|
-
id="fakeId"
|
|
51
58
|
isCustomMetadata
|
|
52
|
-
isEditable
|
|
53
59
|
onCascadeModeChange={jest.fn()}
|
|
54
60
|
onCascadeToggle={jest.fn()}
|
|
55
61
|
shouldShowCascadeOptions
|
|
56
62
|
/>,
|
|
57
63
|
);
|
|
58
|
-
expect(
|
|
64
|
+
expect(
|
|
65
|
+
screen.getByText('Cascade policy cannot be applied to custom metadata at this time.'),
|
|
66
|
+
).toBeInTheDocument();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('should render InlineNotice when isExistingCascadePolicy is true', () => {
|
|
70
|
+
render(<CascadePolicy canEdit isExistingCascadePolicy shouldShowCascadeOptions />);
|
|
71
|
+
expect(
|
|
72
|
+
screen.getByText(
|
|
73
|
+
'This cascade policy cannot be edited. To modify it, deactivate the current policy and then re-enable it to set up a new one.',
|
|
74
|
+
),
|
|
75
|
+
).toBeInTheDocument();
|
|
59
76
|
});
|
|
60
77
|
|
|
61
78
|
test('should render AI folder extraction toggle when canEdit, canUseAIFolderExtraction, and shouldShowCascadeOptions are true', () => {
|
|
@@ -96,60 +113,74 @@ describe('features/metadata-instance-editor/CascadePolicy', () => {
|
|
|
96
113
|
});
|
|
97
114
|
|
|
98
115
|
describe('AI Agent Selector', () => {
|
|
99
|
-
test('should render AI agent selector with default to basic when AI features are enabled', () => {
|
|
116
|
+
test('should render AI agent selector with default to basic when AI features are enabled', async () => {
|
|
100
117
|
render(
|
|
101
118
|
<CascadePolicy
|
|
102
119
|
canEdit
|
|
103
120
|
canUseAIFolderExtraction
|
|
104
121
|
canUseAIFolderExtractionAgentSelector
|
|
105
122
|
shouldShowCascadeOptions
|
|
123
|
+
isAIFolderExtractionEnabled
|
|
124
|
+
onAIFolderExtractionToggle={jest.fn()}
|
|
106
125
|
/>,
|
|
107
126
|
);
|
|
108
|
-
|
|
127
|
+
|
|
128
|
+
const aiToggle = screen.getByRole('switch', { name: 'Box AI Autofill' });
|
|
129
|
+
await userEvent.click(aiToggle); // Enable AI
|
|
130
|
+
|
|
131
|
+
expect(aiToggle).toBeChecked();
|
|
132
|
+
|
|
133
|
+
expect(screen.getByRole('combobox', { name: 'Standard' })).toBeInTheDocument();
|
|
109
134
|
});
|
|
110
135
|
|
|
111
136
|
test('should not render AI agent selector when canUseAIFolderExtractionAgentSelector is false', () => {
|
|
112
137
|
render(<CascadePolicy canEdit canUseAIFolderExtraction shouldShowCascadeOptions />);
|
|
113
|
-
expect(screen.queryByRole('combobox', { name: '
|
|
138
|
+
expect(screen.queryByRole('combobox', { name: 'Standard' })).not.toBeInTheDocument();
|
|
114
139
|
});
|
|
115
|
-
});
|
|
116
140
|
|
|
117
|
-
|
|
118
|
-
|
|
141
|
+
test('should call onAIAgentSelect when an agent is selected', async () => {
|
|
142
|
+
const onAIAgentSelect = jest.fn();
|
|
119
143
|
render(
|
|
120
144
|
<CascadePolicy
|
|
121
145
|
canEdit
|
|
122
146
|
canUseAIFolderExtraction
|
|
147
|
+
canUseAIFolderExtractionAgentSelector
|
|
123
148
|
shouldShowCascadeOptions
|
|
124
|
-
|
|
149
|
+
isAIFolderExtractionEnabled
|
|
150
|
+
onAIAgentSelect={onAIAgentSelect}
|
|
151
|
+
onAIFolderExtractionToggle={jest.fn()}
|
|
125
152
|
/>,
|
|
126
153
|
);
|
|
127
154
|
|
|
128
|
-
const
|
|
129
|
-
|
|
155
|
+
const aiToggle = screen.getByRole('switch', { name: 'Box AI Autofill' });
|
|
156
|
+
await userEvent.click(aiToggle); // Enable AI
|
|
130
157
|
|
|
131
|
-
expect(
|
|
132
|
-
});
|
|
158
|
+
expect(aiToggle).toBeChecked();
|
|
133
159
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<CascadePolicy
|
|
137
|
-
canEdit
|
|
138
|
-
canUseAIFolderExtraction
|
|
139
|
-
shouldShowCascadeOptions
|
|
140
|
-
isExistingAIExtractionCascadePolicy={false}
|
|
141
|
-
/>,
|
|
142
|
-
);
|
|
160
|
+
// Find the combobox by its accessible name
|
|
161
|
+
const combobox = screen.getByRole('combobox', { name: 'Standard' });
|
|
143
162
|
|
|
144
|
-
|
|
145
|
-
|
|
163
|
+
// Open the combobox (simulate user click)
|
|
164
|
+
await userEvent.click(combobox);
|
|
146
165
|
|
|
147
|
-
|
|
166
|
+
// Find the option for "Advanced" and select it
|
|
167
|
+
const option = await screen.findByRole('option', { name: 'Enhanced' });
|
|
168
|
+
await userEvent.click(option);
|
|
169
|
+
|
|
170
|
+
// The expected agent object (should match the one in CascadePolicy.js)
|
|
171
|
+
const expectedAgent = {
|
|
172
|
+
id: '1',
|
|
173
|
+
name: 'Standard',
|
|
174
|
+
isEnterpriseDefault: true,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
expect(onAIAgentSelect).toHaveBeenCalledWith(expectedAgent);
|
|
148
178
|
});
|
|
179
|
+
});
|
|
149
180
|
|
|
181
|
+
describe('AI Autofill Toggle', () => {
|
|
150
182
|
test('should call onAIFolderExtractionToggle when toggle is clicked and enabled', async () => {
|
|
151
183
|
const onAIFolderExtractionToggle = jest.fn();
|
|
152
|
-
|
|
153
184
|
render(
|
|
154
185
|
<CascadePolicy
|
|
155
186
|
canEdit
|
|
@@ -159,35 +190,11 @@ describe('features/metadata-instance-editor/CascadePolicy', () => {
|
|
|
159
190
|
onAIFolderExtractionToggle={onAIFolderExtractionToggle}
|
|
160
191
|
/>,
|
|
161
192
|
);
|
|
162
|
-
|
|
163
193
|
const aiSection = screen.getByTestId('ai-folder-extraction');
|
|
164
194
|
const toggle = within(aiSection).getByRole('switch');
|
|
165
|
-
|
|
166
195
|
await userEvent.click(toggle);
|
|
167
|
-
|
|
168
196
|
expect(onAIFolderExtractionToggle).toHaveBeenCalledTimes(1);
|
|
169
197
|
expect(onAIFolderExtractionToggle).toHaveBeenCalledWith(true);
|
|
170
198
|
});
|
|
171
|
-
|
|
172
|
-
test('should not call onAIFolderExtractionToggle when toggle is clicked but disabled', async () => {
|
|
173
|
-
const onAIFolderExtractionToggle = jest.fn();
|
|
174
|
-
|
|
175
|
-
render(
|
|
176
|
-
<CascadePolicy
|
|
177
|
-
canEdit
|
|
178
|
-
canUseAIFolderExtraction
|
|
179
|
-
shouldShowCascadeOptions
|
|
180
|
-
isExistingAIExtractionCascadePolicy={true}
|
|
181
|
-
onAIFolderExtractionToggle={onAIFolderExtractionToggle}
|
|
182
|
-
/>,
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
const aiSection = screen.getByTestId('ai-folder-extraction');
|
|
186
|
-
const toggle = within(aiSection).getByRole('switch');
|
|
187
|
-
|
|
188
|
-
await userEvent.click(toggle);
|
|
189
|
-
|
|
190
|
-
expect(onAIFolderExtractionToggle).not.toHaveBeenCalled();
|
|
191
|
-
});
|
|
192
199
|
});
|
|
193
200
|
});
|
|
@@ -834,12 +834,17 @@ describe('Instance Component - React Testing Library', () => {
|
|
|
834
834
|
|
|
835
835
|
describe('AI Folder Extraction Toggle Interaction', () => {
|
|
836
836
|
test('should toggle AI folder extraction, disable/enable fields', async () => {
|
|
837
|
-
render(<Instance {...getBaseProps()} />);
|
|
837
|
+
render(<Instance {...getBaseProps({ cascadePolicy: { canEdit: true } })} />);
|
|
838
838
|
|
|
839
839
|
// Click Edit button to enable editing
|
|
840
840
|
const editButton = screen.getByRole('button', { name: 'Edit Metadata' }); // Assuming 'Edit Metadata' is the rendered name
|
|
841
841
|
await userEvent.click(editButton);
|
|
842
842
|
|
|
843
|
+
// Click Enable Cascade Policy
|
|
844
|
+
const metadataCascadeEnable = await screen.findByTestId('metadata-cascade-enable');
|
|
845
|
+
const enableCascadePolicy = await within(metadataCascadeEnable).findByRole('switch');
|
|
846
|
+
await userEvent.click(enableCascadePolicy);
|
|
847
|
+
|
|
843
848
|
// Initially, fields are enabled as default policy is not 'ai_extract'
|
|
844
849
|
expect(screen.getByRole('textbox', { name: 'String Field example of a string field' })).not.toBeDisabled();
|
|
845
850
|
|
|
@@ -858,53 +863,63 @@ describe('Instance Component - React Testing Library', () => {
|
|
|
858
863
|
|
|
859
864
|
describe('Props passed to CascadePolicy', () => {
|
|
860
865
|
test('should pass canUseAIFolderExtractionAgentSelector to CascadePolicy', async () => {
|
|
861
|
-
render(<Instance {...getBaseProps({ canUseAIFolderExtractionAgentSelector: true })} />);
|
|
862
|
-
|
|
863
|
-
const editButton = screen.queryByRole('button', { name: 'Edit Metadata' });
|
|
864
|
-
if (editButton) await userEvent.click(editButton); // Enter edit mode to ensure CascadePolicy options are visible
|
|
865
|
-
|
|
866
|
-
expect(screen.getByRole('combobox', { name: 'Basic' })).toBeInTheDocument();
|
|
867
|
-
});
|
|
868
|
-
|
|
869
|
-
test('should pass isExistingAIExtractionCascadePolicy=true to CascadePolicy if policy is ai_extract', async () => {
|
|
870
866
|
render(
|
|
871
867
|
<Instance
|
|
872
868
|
{...getBaseProps({
|
|
869
|
+
canUseAIFolderExtractionAgentSelector: true,
|
|
873
870
|
cascadePolicy: {
|
|
874
|
-
id: 'policy-
|
|
871
|
+
id: 'policy-1',
|
|
875
872
|
canEdit: true,
|
|
876
873
|
isEnabled: true,
|
|
877
|
-
scope: 'enterprise_123',
|
|
878
874
|
cascadePolicyType: CASCADE_POLICY_TYPE_AI_EXTRACT,
|
|
879
875
|
},
|
|
880
876
|
})}
|
|
881
877
|
/>,
|
|
882
878
|
);
|
|
879
|
+
|
|
883
880
|
const editButton = screen.queryByRole('button', { name: 'Edit Metadata' });
|
|
884
|
-
if (editButton) await userEvent.click(editButton);
|
|
881
|
+
if (editButton) await userEvent.click(editButton); // Enter edit mode to ensure CascadePolicy options are visible
|
|
885
882
|
|
|
886
|
-
const
|
|
887
|
-
|
|
888
|
-
|
|
883
|
+
const cascadeToggle = screen.getByRole('switch', { name: 'Enable Cascade Policy' });
|
|
884
|
+
expect(cascadeToggle).toBeChecked();
|
|
885
|
+
|
|
886
|
+
const aiToggle = screen.getByRole('switch', { name: 'Box AI Autofill' });
|
|
887
|
+
expect(aiToggle).toBeChecked();
|
|
888
|
+
|
|
889
|
+
expect(screen.getByRole('combobox', { name: 'Standard' })).toBeInTheDocument();
|
|
889
890
|
});
|
|
890
891
|
|
|
891
|
-
test('should
|
|
892
|
+
test('should disable CascadePolicy options when a cascade already exists', async () => {
|
|
892
893
|
render(<Instance {...getBaseProps()} />);
|
|
894
|
+
|
|
895
|
+
// Enter edit mode
|
|
893
896
|
const editButton = screen.queryByRole('button', { name: 'Edit Metadata' });
|
|
894
897
|
if (editButton) await userEvent.click(editButton);
|
|
895
898
|
|
|
899
|
+
// Cascade options should be disabled
|
|
900
|
+
const skipRadio = screen.getByLabelText(/skip/i);
|
|
901
|
+
const overwriteRadio = screen.getByLabelText(/overwrite/i);
|
|
902
|
+
expect(skipRadio).toBeDisabled();
|
|
903
|
+
expect(overwriteRadio).toBeDisabled();
|
|
904
|
+
|
|
905
|
+
// AI toggle should be disabled
|
|
896
906
|
const aiSection = screen.getByTestId('ai-folder-extraction');
|
|
897
907
|
const aiToggle = within(aiSection).getByRole('switch');
|
|
898
|
-
expect(aiToggle).
|
|
908
|
+
expect(aiToggle).toBeDisabled();
|
|
899
909
|
});
|
|
900
910
|
});
|
|
901
911
|
|
|
902
912
|
describe('Props passed to TemplatedInstance', () => {
|
|
903
913
|
test('should pass isDisabled=true to TemplatedInstance when AI folder extraction is enabled', async () => {
|
|
904
|
-
render(<Instance {...getBaseProps()} />);
|
|
914
|
+
render(<Instance {...getBaseProps({ cascadePolicy: { canEdit: true } })} />);
|
|
905
915
|
const editButton = screen.getByRole('button', { name: 'Edit Metadata' });
|
|
906
916
|
await userEvent.click(editButton);
|
|
907
917
|
|
|
918
|
+
// Click Enable Cascade Policy
|
|
919
|
+
const metadataCascadeEnable = await screen.findByTestId('metadata-cascade-enable');
|
|
920
|
+
const enableCascadePolicy = await within(metadataCascadeEnable).findByRole('switch');
|
|
921
|
+
await userEvent.click(enableCascadePolicy);
|
|
922
|
+
|
|
908
923
|
const aiSection = screen.getByTestId('ai-folder-extraction');
|
|
909
924
|
const aiToggle = within(aiSection).getByRole('switch');
|
|
910
925
|
|
|
@@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
3
3
|
import { render, screen } from '../../../test-utils/testing-library';
|
|
4
4
|
|
|
5
5
|
import Instances from '../Instances';
|
|
6
|
+
import { CASCADE_POLICY_TYPE_AI_EXTRACT } from '../constants';
|
|
6
7
|
|
|
7
8
|
// Templates
|
|
8
9
|
|
|
@@ -254,18 +255,22 @@ describe('features/metadata-editor-editor/Instances', () => {
|
|
|
254
255
|
|
|
255
256
|
describe('Instances component - canUseAIFolderExtractionAgentSelector prop', () => {
|
|
256
257
|
test('should pass canUseAIFolderExtractionAgentSelector to child Instance components, showing agent selector', async () => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
/>,
|
|
263
|
-
);
|
|
258
|
+
const props = getInstancesBaseProps({
|
|
259
|
+
canUseAIFolderExtractionAgentSelector: true,
|
|
260
|
+
});
|
|
261
|
+
props.editors[0].instance.cascadePolicy.cascadePolicyType = CASCADE_POLICY_TYPE_AI_EXTRACT;
|
|
262
|
+
render(<Instances {...props} />);
|
|
264
263
|
|
|
265
264
|
const editButton = screen.getByRole('button', { name: 'Edit Metadata' });
|
|
266
265
|
await userEvent.click(editButton);
|
|
267
266
|
|
|
268
|
-
|
|
267
|
+
const cascadeToggle = screen.getByRole('switch', { name: 'Enable Cascade Policy' });
|
|
268
|
+
expect(cascadeToggle).toBeChecked();
|
|
269
|
+
|
|
270
|
+
const aiToggle = screen.getByRole('switch', { name: 'Box AI Autofill' });
|
|
271
|
+
expect(aiToggle).toBeChecked();
|
|
272
|
+
|
|
273
|
+
expect(screen.getByRole('combobox', { name: 'Standard' })).toBeInTheDocument();
|
|
269
274
|
});
|
|
270
275
|
|
|
271
276
|
test('should not show agent selector in child Instance if canUseAIFolderExtractionAgentSelector is false', async () => {
|
|
@@ -280,7 +285,7 @@ describe('Instances component - canUseAIFolderExtractionAgentSelector prop', ()
|
|
|
280
285
|
const editButton = screen.getByRole('button', { name: 'Edit Metadata' });
|
|
281
286
|
await userEvent.click(editButton);
|
|
282
287
|
|
|
283
|
-
expect(screen.queryByRole('combobox', { name: '
|
|
288
|
+
expect(screen.queryByRole('combobox', { name: 'Standard' })).not.toBeInTheDocument();
|
|
284
289
|
});
|
|
285
290
|
|
|
286
291
|
test('should not show agent selector in child Instance if canUseAIFolderExtractionAgentSelector is undefined', async () => {
|
|
@@ -291,6 +296,6 @@ describe('Instances component - canUseAIFolderExtractionAgentSelector prop', ()
|
|
|
291
296
|
const editButton = screen.getByRole('button', { name: 'Edit Metadata' });
|
|
292
297
|
await userEvent.click(editButton);
|
|
293
298
|
|
|
294
|
-
expect(screen.queryByRole('combobox', { name: '
|
|
299
|
+
expect(screen.queryByRole('combobox', { name: 'Standard' })).not.toBeInTheDocument();
|
|
295
300
|
});
|
|
296
301
|
});
|