-
-
Notifications
You must be signed in to change notification settings - Fork 28
feat: allow alternative field export format #1517
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,7 +3,7 @@ import { logger } from '@jetstream/shared/client-logger'; | |||||||||||||||||||||||||||
| import { INDEXED_DB, TITLES } from '@jetstream/shared/constants'; | ||||||||||||||||||||||||||||
| import { APP_ROUTES } from '@jetstream/shared/ui-router'; | ||||||||||||||||||||||||||||
| import { useRollbar, useTitle } from '@jetstream/shared/ui-utils'; | ||||||||||||||||||||||||||||
| import { getErrorMessage, getErrorMessageAndStackObj } from '@jetstream/shared/utils'; | ||||||||||||||||||||||||||||
| import { getErrorMessage, getErrorMessageAndStackObj, NOOP } from '@jetstream/shared/utils'; | ||||||||||||||||||||||||||||
| import { SplitWrapper as Split } from '@jetstream/splitjs'; | ||||||||||||||||||||||||||||
| import { DescribeGlobalSObjectResult, ListItem, Maybe } from '@jetstream/types'; | ||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||
|
|
@@ -31,7 +31,9 @@ import { recentHistoryItemsDb } from '@jetstream/ui/db'; | |||||||||||||||||||||||||||
| import { useAtomValue } from 'jotai'; | ||||||||||||||||||||||||||||
| import localforage from 'localforage'; | ||||||||||||||||||||||||||||
| import { Fragment, FunctionComponent, useEffect, useRef, useState } from 'react'; | ||||||||||||||||||||||||||||
| import { prepareMetadataExport } from './sobject-export-metadata-utils'; | ||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||
| ExportFormat, | ||||||||||||||||||||||||||||
| ExportHeaderOption, | ||||||||||||||||||||||||||||
| ExportOptions, | ||||||||||||||||||||||||||||
| ExportWorksheetLayout, | ||||||||||||||||||||||||||||
|
|
@@ -44,6 +46,7 @@ import { | |||||||||||||||||||||||||||
| getAttributes, | ||||||||||||||||||||||||||||
| getChildRelationshipNames, | ||||||||||||||||||||||||||||
| getExtendedFieldDefinitionData, | ||||||||||||||||||||||||||||
| getMetadataAttributes, | ||||||||||||||||||||||||||||
| getSobjectMetadata, | ||||||||||||||||||||||||||||
| prepareExport, | ||||||||||||||||||||||||||||
| } from './sobject-export-utils'; | ||||||||||||||||||||||||||||
|
|
@@ -57,6 +60,16 @@ const FIELD_ATTRIBUTES: ListItem<SobjectExportFieldName>[] = getAttributes().map | |||||||||||||||||||||||||||
| tertiaryLabel, | ||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const METADATA_FIELD_ATTRIBUTES: ListItem<SobjectExportFieldName>[] = getMetadataAttributes().map( | ||||||||||||||||||||||||||||
| ({ label, name, description, tertiaryLabel }) => ({ | ||||||||||||||||||||||||||||
| id: name, | ||||||||||||||||||||||||||||
| label: `${label} (${name})`, | ||||||||||||||||||||||||||||
| value: name, | ||||||||||||||||||||||||||||
| secondaryLabel: description, | ||||||||||||||||||||||||||||
| tertiaryLabel, | ||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const DEFAULT_SELECTION: SobjectExportFieldName[] = [ | ||||||||||||||||||||||||||||
| 'calculatedFormula', | ||||||||||||||||||||||||||||
| 'createable', | ||||||||||||||||||||||||||||
|
|
@@ -75,9 +88,12 @@ const DEFAULT_SELECTION: SobjectExportFieldName[] = [ | |||||||||||||||||||||||||||
| 'updateable', | ||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const DEFAULT_METADATA_SELECTION = METADATA_FIELD_ATTRIBUTES.map((item) => item.value); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const DEFAULT_OPTIONS: ExportOptions = { | ||||||||||||||||||||||||||||
| exportFormat: 'describe', // describe, metadata | ||||||||||||||||||||||||||||
| worksheetLayout: 'combined', // combined, split | ||||||||||||||||||||||||||||
| headerOption: 'label', // label, name | ||||||||||||||||||||||||||||
| headerOption: 'name', // label, name | ||||||||||||||||||||||||||||
| includesStandardFields: true, | ||||||||||||||||||||||||||||
| includeObjectAttributes: false, | ||||||||||||||||||||||||||||
| saveAsDefaultSelection: false, | ||||||||||||||||||||||||||||
|
|
@@ -93,6 +109,7 @@ export const SObjectExport: FunctionComponent<SObjectExportProps> = () => { | |||||||||||||||||||||||||||
| const { google_apiKey, google_appId, google_clientId } = useAtomValue(applicationCookieState); | ||||||||||||||||||||||||||||
| const { hasGoogleDriveAccess, googleShowUpgradeToPro } = useAtomValue(googleDriveAccessState); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const picklistExportFormatRef = useRef<PicklistRef>(null); | ||||||||||||||||||||||||||||
| const picklistWorksheetLayoutRef = useRef<PicklistRef>(null); | ||||||||||||||||||||||||||||
| const picklistHeaderOptionRef = useRef<PicklistRef>(null); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
@@ -109,17 +126,29 @@ export const SObjectExport: FunctionComponent<SObjectExportProps> = () => { | |||||||||||||||||||||||||||
| const [hasSelectionsMade, setHasSelectionsMade] = useState(false); | ||||||||||||||||||||||||||||
| const [options, setOptions] = useState<ExportOptions>({ ...DEFAULT_OPTIONS }); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const isMetadataExport = options.exportFormat === 'metadata'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||
| setSobjects(null); | ||||||||||||||||||||||||||||
| setSelectedSObjects([]); | ||||||||||||||||||||||||||||
| }, [selectedOrg]); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||
| if (isMetadataExport) { | ||||||||||||||||||||||||||||
| picklistHeaderOptionRef.current?.selectItem('name'); | ||||||||||||||||||||||||||||
| picklistWorksheetLayoutRef.current?.selectItem('split'); | ||||||||||||||||||||||||||||
|
Comment on lines
+137
to
+139
|
||||||||||||||||||||||||||||
| if (isMetadataExport) { | |
| picklistHeaderOptionRef.current?.selectItem('name'); | |
| picklistWorksheetLayoutRef.current?.selectItem('split'); | |
| if (isMetadataExport) { | |
| // Ensure the UI picklists show the correct options for metadata exports | |
| picklistHeaderOptionRef.current?.selectItem('name'); | |
| picklistWorksheetLayoutRef.current?.selectItem('split'); | |
| // Keep underlying options state in sync with the picklist selections | |
| setOptions((prev) => ({ | |
| ...prev, | |
| headerOption: 'name', | |
| worksheetLayout: 'split', | |
| })); |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hasSelectionsMade check requires selectedAttributes to have values, but in metadata export mode, the attribute selection is disabled and fixed. The metadata export doesn't actually use selectedAttributes (it uses allFields directly), so requiring selectedAttributes to have values for metadata exports doesn't make logical sense. Consider updating the hasSelectionsMade logic to skip the selectedAttributes check when isMetadataExport is true: setHasSelectionsMade(!!selectedSObjects?.length && (isMetadataExport || !!selectedAttributes?.length))
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The notification message doesn't mention that only custom fields (not standard fields) are exported in metadata format. Users might be confused when they don't see standard fields in the export. Consider updating the message to clarify this limitation, for example: "Metadata format will include all custom field metadata and can be used as an import template in the Create Object and Fields feature. Note: Only custom fields are exported in this format."
| Metadata format will include all metadata fields and can be used as an import template in the Create Object and Fields | |
| feature. | |
| Metadata format will include all custom field metadata and can be used as an import template in the Create Object and Fields | |
| feature. Note: Only custom fields are exported in this format. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default headerOption was changed from 'label' to 'name', which changes the default behavior for all users. This could be unexpected for existing users who rely on the previous default of 'label'. Consider either reverting this change to maintain backward compatibility, or documenting this breaking change in the PR description and release notes. If the intent is to use 'name' as the default only for metadata exports, this should be handled conditionally rather than changing the global default.