Skip to content

Commit d84b440

Browse files
authored
Merge branch 'master' into remove-hotkey-legacy-context
2 parents 20d25d0 + f76ed73 commit d84b440

25 files changed

+608
-119
lines changed

flow-typed/npm/@box/blueprint-web-assets_vx.x.x.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ declare module '@box/blueprint-web-assets/icons/Medium' {
77
declare export var AlertCircle: React$ComponentType<any>;
88
declare export var AlertTriangle: React$ComponentType<any>;
99
declare export var XMark: React$ComponentType<any>;
10-
declare export var BoxAiAdvancedLogo24: React$ComponentType<any>;
11-
declare export var BoxAiLogo24: React$ComponentType<any>;
10+
declare export var Comment: React$ComponentType<any>;
11+
declare export var Metadata: React$ComponentType<any>;
12+
}
13+
14+
declare module '@box/blueprint-web-assets/icons/MediumFilled' {
15+
declare export var Comment: React$ComponentType<any>;
16+
declare export var InformationCircle: React$ComponentType<any>;
17+
declare export var Metadata: React$ComponentType<any>;
1218
}
1319

1420
declare module '@box/blueprint-web-assets/icons/Content' {
@@ -37,6 +43,7 @@ declare module '@box/blueprint-web-assets/icons/Fill' {
3743
declare module '@box/blueprint-web-assets/icons/Logo' {
3844
declare export var BoxLogo: React$ComponentType<any>;
3945
declare export var BoxAiLogo: React$ComponentType<any>;
46+
declare export var BoxAiLogo24: React$ComponentType<any>;
4047
}
4148

4249
declare module '@box/blueprint-web-assets/illustrations/Medium' {
@@ -56,4 +63,5 @@ declare module '@box/blueprint-web-assets/tokens/tokens' {
5663
declare export var Gray50: any;
5764
declare export var Size3: any;
5865
declare export var Size6: any;
66+
declare export var IconIconBlue: any;
5967
}

src/api/Metadata.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import getProp from 'lodash/get';
1212
import isEmpty from 'lodash/isEmpty';
1313
import keyBy from 'lodash/keyBy';
1414
import lodashMap from 'lodash/map';
15-
import partition from 'lodash/partition';
1615
import uniq from 'lodash/uniq';
1716
import uniqueId from 'lodash/uniqueId';
1817
import { getBadItemError, getBadPermissionsError, isUserCorrectableError } from '../utils/error';
@@ -288,14 +287,9 @@ class Metadata extends File {
288287
return lodashMap(templates, template => {
289288
if (!template.fields) return template;
290289
291-
const [fieldsToUpdate, restFields] = partition(
292-
template.fields,
293-
field => field.type === 'taxonomy' && !field.levels,
294-
);
295-
296-
if (isEmpty(fieldsToUpdate)) return template;
290+
const updatedFields = lodashMap(template.fields, field => {
291+
if (field.type !== 'taxonomy' || field.levels) return field;
297292
298-
const updatedFields = lodashMap(fieldsToUpdate, field => {
299293
const taxonomyPath = this.getTaxonomyPath(field.namespace, field.taxonomyKey || field.taxonomy_key);
300294
const levels = taxonomyInfo[taxonomyPath]?.levels || [];
301295
@@ -315,7 +309,7 @@ class Metadata extends File {
315309
316310
return {
317311
...template,
318-
fields: restFields.concat(updatedFields),
312+
fields: updatedFields,
319313
};
320314
});
321315
}

src/api/__tests__/Metadata.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,6 @@ describe('api/Metadata', () => {
298298
id: 1,
299299
hidden: false,
300300
fields: [
301-
{ type: 'string' },
302301
{
303302
type: 'taxonomy',
304303
namespace: 'namespace1',
@@ -308,6 +307,7 @@ describe('api/Metadata', () => {
308307
{ displayName: 'level 2', description: 'Another level' },
309308
],
310309
},
310+
{ type: 'string' },
311311
],
312312
},
313313
{ id: 2, hidden: false, fields: [{ type: 'string', namespace: 'namespace2' }] },
@@ -352,7 +352,6 @@ describe('api/Metadata', () => {
352352
id: 1,
353353
hidden: false,
354354
fields: [
355-
{ type: 'string' },
356355
{
357356
type: 'taxonomy',
358357
namespace: 'namespace1',
@@ -362,6 +361,7 @@ describe('api/Metadata', () => {
362361
{ displayName: 'level 2', description: 'Another level' },
363362
],
364363
},
364+
{ type: 'string' },
365365
],
366366
},
367367
{ id: 2, hidden: false, fields: [{ type: 'string', namespace: 'namespace2' }] },

src/components/sidebar-toggle-button/SidebarToggleButton.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import * as React from 'react';
33
import classNames from 'classnames';
44
import { injectIntl } from 'react-intl';
55
import type { IntlShape } from 'react-intl';
6-
6+
import { Tooltip as BPTooltip } from '@box/blueprint-web';
77
import IconHide from '../../icons/general/IconHide';
88
import IconShow from '../../icons/general/IconShow';
99
import PlainButton from '../plain-button';
1010
import Tooltip from '../tooltip';
11-
11+
import { useFeatureConfig } from '../../elements/common/feature-checking';
1212
import messages from '../../elements/common/messages';
1313

1414
import './SidebarToggleButton.scss';
@@ -32,21 +32,36 @@ const SidebarToggleButton = ({
3232
onClick,
3333
...rest
3434
}: Props) => {
35+
const { enabled: isPreviewModernizationEnabled } = useFeatureConfig('previewModernization');
3536
const isCollapsed = !isOpen ? 'collapsed' : '';
3637
const intlMessage = isOpen ? messages.sidebarHide : messages.sidebarShow;
3738
const intlText = intl.formatMessage(intlMessage);
3839
const classes = classNames(className, 'bdl-SidebarToggleButton', {
3940
'bdl-is-collapsed': isCollapsed,
41+
'bdl-SidebarToggleButton--modernized': isPreviewModernizationEnabled,
4042
});
4143
const tooltipPosition = direction === DIRECTION_LEFT ? 'middle-right' : 'middle-left';
42-
4344
const renderButton = () => {
4445
if (direction === DIRECTION_LEFT) {
4546
return isOpen ? <IconShow height={16} width={16} /> : <IconHide height={16} width={16} />;
4647
}
4748
return isOpen ? <IconHide height={16} width={16} /> : <IconShow height={16} width={16} />;
4849
};
4950

51+
if (isPreviewModernizationEnabled) {
52+
const tooltipPositionModernized = direction === DIRECTION_LEFT ? DIRECTION_RIGHT : DIRECTION_LEFT;
53+
54+
return (
55+
<BPTooltip content={intlText} side={tooltipPositionModernized}>
56+
{/* Workaround to attach BP tooltip to legacy button, remove span when buttons are migrated to BP */}
57+
<span>
58+
<PlainButton aria-label={intlText} className={classes} onClick={onClick} type="button" {...rest}>
59+
{renderButton()}
60+
</PlainButton>
61+
</span>
62+
</BPTooltip>
63+
);
64+
}
5065
return (
5166
<Tooltip position={tooltipPosition} text={intlText}>
5267
<PlainButton aria-label={intlText} className={classes} onClick={onClick} type="button" {...rest}>

src/components/sidebar-toggle-button/SidebarToggleButton.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,17 @@
2929
}
3030
}
3131
}
32+
33+
.bcs-SidebarNav--modernized {
34+
.bdl-SidebarToggleButton {
35+
&:not(.bdl-is-disabled):hover svg,
36+
&:not(.is-disabled):hover svg,
37+
&:not(.bdl-is-disabled):focus svg,
38+
&:not(.is-disabled):focus svg,
39+
&.bdl-is-collapsed svg,
40+
&.bdl-is-collapsed:hover svg {
41+
transform: scale(1.09);
42+
transition: transform 150ms;
43+
}
44+
}
45+
}

src/elements/content-sidebar/AddTaskButton.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
import * as React from 'react';
33
import { type RouterHistory } from 'react-router-dom';
44
import { withRouterIfEnabled } from '../common/routing';
5+
import { withFeatureConsumer, getFeatureConfig } from '../common/feature-checking';
6+
import type { FeatureConfig } from '../common/feature-checking';
57

68
import AddTaskMenu from './AddTaskMenu';
9+
import AddTaskMenuV2 from './AddTaskMenuV2';
710
import TaskModal from './TaskModal';
811
import { TASK_TYPE_APPROVAL } from '../../constants';
912
import type { TaskFormProps } from './activity-feed/task-form/TaskForm';
@@ -12,6 +15,7 @@ import type { ElementsXhrError } from '../../common/types/api';
1215
import type { InternalSidebarNavigation, InternalSidebarNavigationHandler } from '../common/types/SidebarNavigation';
1316

1417
type Props = {|
18+
features: FeatureConfig,
1519
history?: RouterHistory,
1620
internalSidebarNavigation?: InternalSidebarNavigation,
1721
internalSidebarNavigationHandler?: InternalSidebarNavigationHandler,
@@ -79,16 +83,24 @@ class AddTaskButton extends React.Component<Props, State> {
7983
};
8084

8185
render() {
82-
const { isDisabled, taskFormProps } = this.props;
86+
const { features, isDisabled, taskFormProps } = this.props;
8387
const { isTaskFormOpen, taskType, error } = this.state;
88+
const featureConfig = getFeatureConfig(features, 'previewModernization');
89+
const { enabled: isPreviewModernizationEnabled } = featureConfig || {};
90+
91+
const addTaskMenuProps = {
92+
isDisabled,
93+
onMenuItemClick: this.handleClickMenuItem,
94+
setAddTaskButtonRef: this.setAddTaskButtonRef,
95+
};
8496

8597
return (
8698
<>
87-
<AddTaskMenu
88-
isDisabled={isDisabled}
89-
onMenuItemClick={this.handleClickMenuItem}
90-
setAddTaskButtonRef={this.setAddTaskButtonRef}
91-
/>
99+
{isPreviewModernizationEnabled ? (
100+
<AddTaskMenuV2 {...addTaskMenuProps} />
101+
) : (
102+
<AddTaskMenu {...addTaskMenuProps} />
103+
)}
92104
<TaskModal
93105
error={error}
94106
onSubmitError={this.handleSubmitError}
@@ -104,4 +116,4 @@ class AddTaskButton extends React.Component<Props, State> {
104116
}
105117

106118
export { AddTaskButton as AddTaskButtonComponent };
107-
export default withRouterIfEnabled(AddTaskButton);
119+
export default withFeatureConsumer(withRouterIfEnabled(AddTaskButton));
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
$bcs-AddTaskMenu-v-two-width: 256px;
2+
3+
.bcs-AddTaskMenu-v-two {
4+
padding: var(--space-2);
5+
}
6+
7+
.bcs-AddTaskMenu-v-two-menuItem {
8+
display: flex;
9+
max-width: $bcs-AddTaskMenu-v-two-width;
10+
white-space: normal;
11+
gap: var(--space-3);
12+
}
13+
14+
.bcs-AddTaskMenu-v-two-icon {
15+
align-self: center;
16+
background-color: var(--gray-05);
17+
height: var(--size-8);
18+
width: var(--size-8);
19+
border-radius: var(--radius-2);
20+
display: flex;
21+
align-items: center;
22+
justify-content: center;
23+
flex-shrink: 0;
24+
}
25+
26+
.bcs-AddTaskMenu-v-two-title {
27+
font-weight: bold;
28+
}
29+
30+
.bcs-AddTaskMenu-v-two-description {
31+
color: var(--gray-65);
32+
font-size: var(--caption-default-font-size);
33+
line-height: var(--caption-default-line-height);
34+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import * as React from 'react';
2+
import { useIntl } from 'react-intl';
3+
4+
import { DropdownMenu, TriggerButton } from '@box/blueprint-web';
5+
import { ApprovalTask } from '@box/blueprint-web-assets/icons/Fill';
6+
import { Tasks } from '@box/blueprint-web-assets/icons/MediumFilled';
7+
import messages from './messages';
8+
import { TASK_TYPE_APPROVAL, TASK_TYPE_GENERAL } from '../../constants';
9+
import type { TaskType } from '../../common/types/tasks';
10+
11+
import './AddTaskMenuV2.scss';
12+
13+
export interface AddTaskMenuV2Props {
14+
isDisabled: boolean;
15+
onMenuItemClick: (taskType: TaskType) => void;
16+
setAddTaskButtonRef?: (element: HTMLButtonElement | null) => void;
17+
}
18+
19+
const AddTaskMenuV2: React.FC<AddTaskMenuV2Props> = ({ isDisabled, onMenuItemClick, setAddTaskButtonRef }) => {
20+
const { formatMessage } = useIntl();
21+
22+
const [isOpen, setIsOpen] = React.useState(false);
23+
24+
const handleMenuItemClick = React.useCallback(
25+
(taskType: TaskType) => {
26+
// Open the modal first
27+
onMenuItemClick(taskType);
28+
// Then close the dropdown. We rely on onCloseAutoFocus to prevent focus restoration.
29+
setIsOpen(false);
30+
},
31+
[onMenuItemClick],
32+
);
33+
34+
return (
35+
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
36+
<DropdownMenu.Trigger>
37+
<TriggerButton
38+
variant="secondary"
39+
disabled={isDisabled}
40+
ref={setAddTaskButtonRef}
41+
caretDirection={isOpen ? 'up' : 'down'}
42+
label={formatMessage(messages.tasksAddTask)}
43+
/>
44+
</DropdownMenu.Trigger>
45+
46+
<DropdownMenu.Content
47+
align="end"
48+
className="bcs-AddTaskMenu-v-two"
49+
onCloseAutoFocus={(event: Event) => {
50+
// Prevent focus from returning to the trigger button when the menu closes.
51+
// This allows the Modal (which was just opened) to keep focus on its input field
52+
// without having it stolen back, which would trigger a blur validation error.
53+
event.preventDefault();
54+
}}
55+
>
56+
<DropdownMenu.Item onClick={() => handleMenuItemClick(TASK_TYPE_GENERAL)}>
57+
<div className="bcs-AddTaskMenu-v-two-menuItem">
58+
<div className="bcs-AddTaskMenu-v-two-icon">
59+
<Tasks color="black" width={20} height={20} />
60+
</div>
61+
<div>
62+
<div className="bcs-AddTaskMenu-v-two-title">
63+
{formatMessage(messages.taskAddTaskGeneral)}
64+
</div>
65+
<div className="bcs-AddTaskMenu-v-two-description">
66+
{formatMessage(messages.taskAddTaskGeneralDescription)}
67+
</div>
68+
</div>
69+
</div>
70+
</DropdownMenu.Item>
71+
<DropdownMenu.Item onClick={() => handleMenuItemClick(TASK_TYPE_APPROVAL)}>
72+
<div className="bcs-AddTaskMenu-v-two-menuItem">
73+
<div className="bcs-AddTaskMenu-v-two-icon">
74+
{/* Should be replaced by icons/MediumFilled/ApprovalTask after it will be availabel */}
75+
<ApprovalTask color="black" width={20} height={20} />
76+
</div>
77+
<div>
78+
<div className="bcs-AddTaskMenu-v-two-title">
79+
{formatMessage(messages.taskAddTaskApproval)}
80+
</div>
81+
<div className="bcs-AddTaskMenu-v-two-description">
82+
{formatMessage(messages.taskAddTaskApprovalDescription)}
83+
</div>
84+
</div>
85+
</div>
86+
</DropdownMenu.Item>
87+
</DropdownMenu.Content>
88+
</DropdownMenu.Root>
89+
);
90+
};
91+
92+
export default AddTaskMenuV2;

0 commit comments

Comments
 (0)