From ad3635081c71830a05ac21b242dd7f9c83925e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Pi=C4=99ko=C5=9B?= Date: Thu, 19 Feb 2026 17:49:03 +0100 Subject: [PATCH 1/7] feat(classification): migrate Classification to Blueprint for sidebar redesign --- src/features/classification/Classification.js | 86 +++++++++++++++---- .../classification/Classification.scss | 13 +++ .../security-controls/SecurityControls.js | 72 +++++++++++----- .../security-controls/SecurityControls.scss | 5 ++ .../security-controls/SecurityControlsItem.js | 47 ++++++---- 5 files changed, 167 insertions(+), 56 deletions(-) diff --git a/src/features/classification/Classification.js b/src/features/classification/Classification.js index f5e9915cbf..e72a9cc21d 100644 --- a/src/features/classification/Classification.js +++ b/src/features/classification/Classification.js @@ -1,7 +1,11 @@ // @flow import * as React from 'react'; import { FormattedDate, FormattedMessage } from 'react-intl'; +import { Status, Text } from '@box/blueprint-web'; +import { Shield } from '@box/blueprint-web-assets/icons/Line'; +import { GreenLight50 } from '@box/blueprint-web-assets/tokens/tokens'; +import classNames from 'classnames'; import AsyncLoad from '../../elements/common/async-load'; import ClassifiedBadge from './ClassifiedBadge'; import Label from '../../components/label/Label'; @@ -33,6 +37,7 @@ type Props = { controlsFormat?: ControlsFormat, definition?: string, isImportedClassification?: boolean, + isRedesignEnabled?: boolean, isLoadingAppliedBy?: boolean, isLoadingControls?: boolean, itemName?: string, @@ -54,6 +59,7 @@ const Classification = ({ controlsFormat, definition, isImportedClassification = false, + isRedesignEnabled = false, isLoadingAppliedBy = false, isLoadingControls, itemName = '', @@ -118,36 +124,77 @@ const Classification = ({ ); } - return ( + return isRedesignEnabled ? ( + + {modifiedByDetails} + + ) : (

{modifiedByDetails}

); }; + const Wrapper = isRedesignEnabled ? 'div' : 'article'; + return ( -
- {isClassified && ( - - )} - {isInlineMessageEnabled && ( - - )} + + {isClassified && + (isRedesignEnabled ? ( + + ) : ( + + ))} + {isInlineMessageEnabled && + (isRedesignEnabled ? ( +
+ + + + + {definition} + +
+ ) : ( + + ))} {isNotClassifiedMessageVisible && ( )} - {shouldRenderModificationDetails && ( - - )} + {shouldRenderModificationDetails && + (isRedesignEnabled ? ( +
+ + + +
{renderModificationDetails()}
+
+ ) : ( + + ))} {isSecurityControlsEnabled && ( )} {isControlsIndicatorEnabled && } -
+ ); }; diff --git a/src/features/classification/Classification.scss b/src/features/classification/Classification.scss index d4c1363461..906657e465 100644 --- a/src/features/classification/Classification.scss +++ b/src/features/classification/Classification.scss @@ -8,6 +8,13 @@ } } +.bdl-Classification--redesign { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--space-4); +} + .bdl-Classification-definition { max-height: $bdl-line-height * 5; margin-top: 3px; @@ -23,3 +30,9 @@ margin-bottom: var(--space-5); margin-top: var(--space-05); } + +.bdl-Classification-propertySection { + .bdl-Classification-sectionLabel { + margin-block-end: var(--space-1); + } +} diff --git a/src/features/classification/security-controls/SecurityControls.js b/src/features/classification/security-controls/SecurityControls.js index 9a9f2dc56b..760ace2297 100644 --- a/src/features/classification/security-controls/SecurityControls.js +++ b/src/features/classification/security-controls/SecurityControls.js @@ -1,6 +1,7 @@ // @flow import * as React from 'react'; import { FormattedMessage } from 'react-intl'; +import { Text, TextButton } from '@box/blueprint-web'; import { DEFAULT_MAX_APP_COUNT, SECURITY_CONTROLS_FORMAT } from '../constants'; import { getShortSecurityControlsMessage, getFullSecurityControlsMessages } from './utils'; @@ -21,6 +22,7 @@ type Props = { controls: Controls, controlsFormat: ControlsFormat, definition?: string, + isRedesignEnabled?: boolean, itemName?: string, maxAppCount?: number, shouldRenderLabel?: boolean, @@ -35,6 +37,7 @@ class SecurityControls extends React.Component { static defaultProps = { classificationName: '', definition: '', + isRedesignEnabled: false, itemName: '', controls: {}, controlsFormat: SHORT, @@ -58,6 +61,7 @@ class SecurityControls extends React.Component { controls, controlsFormat, definition, + isRedesignEnabled, itemName, maxAppCount, shouldRenderLabel, @@ -81,41 +85,67 @@ class SecurityControls extends React.Component { return null; } - const { isSecurityControlsModalOpen } = this.state; - const shouldShowSecurityControlsModal = - controlsFormat === SHORT_WITH_BTN && !!itemName && !!classificationName && !!definition; - let itemsList = (
    {items.map(({ message, tooltipMessage }, index) => ( - + ))}
); + const { isSecurityControlsModalOpen } = this.state; + const shouldShowSecurityControlsModal = + controlsFormat === SHORT_WITH_BTN && !!itemName && !!classificationName && !!definition; + + const securityControlsModal = shouldShowSecurityControlsModal && ( + + ); + if (shouldRenderLabel) { - itemsList = ; + itemsList = isRedesignEnabled ? ( +
+ + + + {itemsList} + {shouldShowSecurityControlsModal && ( + + + + )} +
+ ) : ( + + ); } return ( <> {itemsList} - {shouldShowSecurityControlsModal && ( - <> - - - - - + {!isRedesignEnabled && shouldShowSecurityControlsModal && ( + + + )} + {securityControlsModal} ); } diff --git a/src/features/classification/security-controls/SecurityControls.scss b/src/features/classification/security-controls/SecurityControls.scss index 926417987e..16921d2a15 100644 --- a/src/features/classification/security-controls/SecurityControls.scss +++ b/src/features/classification/security-controls/SecurityControls.scss @@ -4,3 +4,8 @@ margin: 0; list-style: none; } + +.bdl-SecurityControls-viewAllButton { + padding: 0; + margin-block-start: var(--space-1); +} diff --git a/src/features/classification/security-controls/SecurityControlsItem.js b/src/features/classification/security-controls/SecurityControlsItem.js index 06faf31da1..0e4265b125 100644 --- a/src/features/classification/security-controls/SecurityControlsItem.js +++ b/src/features/classification/security-controls/SecurityControlsItem.js @@ -1,6 +1,8 @@ // @flow import * as React from 'react'; import { FormattedMessage } from 'react-intl'; +import { Text } from '@box/blueprint-web'; +import classNames from 'classnames'; import { bdlBoxBlue } from '../../../styles/variables'; import Tooltip from '../../../components/tooltip'; @@ -9,26 +11,39 @@ import type { MessageItem } from '../flowTypes'; import './SecurityControlsItem.scss'; -type Props = MessageItem; +type Props = { + ...MessageItem, + isRedesignEnabled?: boolean, +}; const ICON_SIZE = 13; -const SecurityControlsItem = ({ message, tooltipMessage }: Props) => { +const SecurityControlsItem = ({ isRedesignEnabled = false, message, tooltipMessage }: Props) => { + const messageContent = React.isValidElement(message) ? message : ; + return ( -
  • - {/* $FlowFixMe */} - {React.isValidElement(message) ? message : } - {tooltipMessage && ( - } - position="middle-right" - isTabbable={false} - > - - - - +
  • + {isRedesignEnabled ? ( + + {messageContent} + + ) : ( + <> + {/* $FlowFixMe */} + {messageContent} + {tooltipMessage && ( + } + position="middle-right" + isTabbable={false} + > + + + + + )} + )}
  • ); From 2da2ac4faf1de578d1066fea4ddc03f486fba8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Pi=C4=99ko=C5=9B?= Date: Thu, 19 Feb 2026 17:53:30 +0100 Subject: [PATCH 2/7] test(classification): update SecurityControls snapshots for redesign prop --- .../__tests__/__snapshots__/SecurityControls.test.js.snap | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/features/classification/security-controls/__tests__/__snapshots__/SecurityControls.test.js.snap b/src/features/classification/security-controls/__tests__/__snapshots__/SecurityControls.test.js.snap index 6d8122cb65..8b2aa9231a 100644 --- a/src/features/classification/security-controls/__tests__/__snapshots__/SecurityControls.test.js.snap +++ b/src/features/classification/security-controls/__tests__/__snapshots__/SecurityControls.test.js.snap @@ -6,6 +6,7 @@ exports[`features/classification/security-controls/SecurityControls should rende className="bdl-SecurityControls" > Date: Thu, 19 Feb 2026 18:02:27 +0100 Subject: [PATCH 3/7] feat(classification): add Classification storybook stories for legacy and redesign --- .../classification/Classification.stories.tsx | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/features/classification/Classification.stories.tsx diff --git a/src/features/classification/Classification.stories.tsx b/src/features/classification/Classification.stories.tsx new file mode 100644 index 0000000000..982459494a --- /dev/null +++ b/src/features/classification/Classification.stories.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; + +import Classification, { STYLE_INLINE } from './Classification'; +import { SECURITY_CONTROLS_FORMAT } from './constants'; + +const definition = + '[Prohibits external access] Internal data is not accessible by external users and can only be accessed by Box employees and contractors. Examples: New hire documents, policies, procedures, announcements, and Boxer training.'; + +const controls = { + boxSignRequest: { enabled: true }, + download: { desktop: { restrictManagedUsers: 'ownersCoOwners' } }, + sharedLink: { accessLevel: 'collabOnly' }, +}; + +const baseProps = { + definition, + messageStyle: STYLE_INLINE, + name: 'BOX-ONLY', + modifiedAt: '2025-01-15T10:30:00Z', + modifiedBy: 'Robert Robertson', +}; + +export const Legacy = () => ; + +export const Redesigned = () => ; + +export const RedesignedWithControls = () => ( + +); + +export const LegacyWithControls = () => ( + +); + +export default { + title: 'Features/Classification', + component: Classification, + decorators: [(story: () => React.ReactNode) =>
    {story()}
    ], +}; From 9f903e57a605e03bb84cca07087687cbc2ffac9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Pi=C4=99ko=C5=9B?= Date: Thu, 19 Feb 2026 18:09:16 +0100 Subject: [PATCH 4/7] feat(classification): add AI classification stories and remove appliedByAiDetails class in redesign --- src/features/classification/Classification.js | 2 +- .../classification/Classification.stories.tsx | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/features/classification/Classification.js b/src/features/classification/Classification.js index e72a9cc21d..2068e4cbc3 100644 --- a/src/features/classification/Classification.js +++ b/src/features/classification/Classification.js @@ -118,7 +118,7 @@ const Classification = ({ ); diff --git a/src/features/classification/Classification.stories.tsx b/src/features/classification/Classification.stories.tsx index 982459494a..26cb4d1bd6 100644 --- a/src/features/classification/Classification.stories.tsx +++ b/src/features/classification/Classification.stories.tsx @@ -34,6 +34,40 @@ export const RedesignedWithControls = () => ( /> ); +const aiClassificationReason = { + answer: 'This document contains internal policies, onboarding procedures, and employee training materials that are restricted to Box employees and contractors only.', + modifiedAt: '2025-01-15T10:30:00Z', + citations: [ + { content: 'New hire onboarding policy v3', fileId: '123', location: 'Page 1', title: 'Onboarding Policy.pdf' }, + { content: 'Internal training schedule 2025', fileId: '456', location: 'Page 2', title: 'Training Guide.docx' }, + ], +}; + +export const RedesignedWithAiClassification = () => ( + +); + +export const RedesignedFull = () => ( + +); + +export const LegacyWithAiClassification = () => ( + +); + export const LegacyWithControls = () => ( Date: Thu, 19 Feb 2026 18:28:22 +0100 Subject: [PATCH 5/7] feat(classification): use dynamic color for redesigned Status badge --- src/features/classification/Classification.js | 3 +-- src/features/classification/Classification.stories.tsx | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/classification/Classification.js b/src/features/classification/Classification.js index 2068e4cbc3..131d96565f 100644 --- a/src/features/classification/Classification.js +++ b/src/features/classification/Classification.js @@ -3,7 +3,6 @@ import * as React from 'react'; import { FormattedDate, FormattedMessage } from 'react-intl'; import { Status, Text } from '@box/blueprint-web'; import { Shield } from '@box/blueprint-web-assets/icons/Line'; -import { GreenLight50 } from '@box/blueprint-web-assets/tokens/tokens'; import classNames from 'classnames'; import AsyncLoad from '../../elements/common/async-load'; @@ -145,7 +144,7 @@ const Classification = ({ > {isClassified && (isRedesignEnabled ? ( - + ) : ( Date: Thu, 19 Feb 2026 18:40:14 +0100 Subject: [PATCH 6/7] refactor(classification): use classNames util and remove unnecessary type cast --- src/features/classification/Classification.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/classification/Classification.js b/src/features/classification/Classification.js index 131d96565f..37110b6bb4 100644 --- a/src/features/classification/Classification.js +++ b/src/features/classification/Classification.js @@ -117,7 +117,7 @@ const Classification = ({ ); @@ -144,7 +144,7 @@ const Classification = ({ > {isClassified && (isRedesignEnabled ? ( - + ) : ( Date: Thu, 19 Feb 2026 19:03:59 +0100 Subject: [PATCH 7/7] fix(classification): resolve Flow type errors in SecurityControlsItem and Classification --- src/features/classification/Classification.js | 1 + .../classification/security-controls/SecurityControlsItem.js | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features/classification/Classification.js b/src/features/classification/Classification.js index 37110b6bb4..ecd8ce48d6 100644 --- a/src/features/classification/Classification.js +++ b/src/features/classification/Classification.js @@ -2,6 +2,7 @@ import * as React from 'react'; import { FormattedDate, FormattedMessage } from 'react-intl'; import { Status, Text } from '@box/blueprint-web'; +// $FlowFixMe - blueprint-web-assets icons not typed for Flow import { Shield } from '@box/blueprint-web-assets/icons/Line'; import classNames from 'classnames'; diff --git a/src/features/classification/security-controls/SecurityControlsItem.js b/src/features/classification/security-controls/SecurityControlsItem.js index 0e4265b125..5b767f4d72 100644 --- a/src/features/classification/security-controls/SecurityControlsItem.js +++ b/src/features/classification/security-controls/SecurityControlsItem.js @@ -11,14 +11,14 @@ import type { MessageItem } from '../flowTypes'; import './SecurityControlsItem.scss'; -type Props = { - ...MessageItem, +type Props = MessageItem & { isRedesignEnabled?: boolean, }; const ICON_SIZE = 13; const SecurityControlsItem = ({ isRedesignEnabled = false, message, tooltipMessage }: Props) => { + // $FlowFixMe const messageContent = React.isValidElement(message) ? message : ; return ( @@ -29,7 +29,6 @@ const SecurityControlsItem = ({ isRedesignEnabled = false, message, tooltipMessa ) : ( <> - {/* $FlowFixMe */} {messageContent} {tooltipMessage && (