Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/compass-collection/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"reformat": "npm run eslint . -- --fix && npm run prettier -- --write ."
},
"dependencies": {
"@faker-js/faker": "^9.0.0",
"@mongodb-js/compass-app-registry": "^9.4.22",
"@mongodb-js/compass-app-stores": "^7.59.0",
"@mongodb-js/compass-components": "^1.51.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {
Banner,
BannerVariant,
Body,
css,
Option,
palette,
Select,
spacing,
} from '@mongodb-js/compass-components';
import React from 'react';
import { UNRECOGNIZED_FAKER_METHOD } from '../../modules/collection-tab';

const fieldMappingSelectorsStyles = css({
width: '50%',
display: 'flex',
flexDirection: 'column',
gap: spacing[200],
});

const labelStyles = css({
color: palette.gray.dark1,
fontWeight: 600,
});

interface Props {
activeJsonType: string;
activeFakerFunction: string;
onJsonTypeSelect: (jsonType: string) => void;
onFakerFunctionSelect: (fakerFunction: string) => void;
}

const FakerMappingSelector = ({
activeJsonType,
activeFakerFunction,
onJsonTypeSelect,
onFakerFunctionSelect,
}: Props) => {
return (
<div className={fieldMappingSelectorsStyles}>
<Body className={labelStyles}>Mapping</Body>
<Select
Copy link
Collaborator

@gribnoysup gribnoysup Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These select boxes allow to deselect the value, and as soon as I deselect it, the whole input control disappears and I can't select anything anymore, that's probably not an expected behavior?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.

label="JSON Type"
allowDeselect={false}
value={activeJsonType}
onChange={onJsonTypeSelect}
>
{/* TODO(CLOUDP-344400) : Make the select input editable and render other options depending on the JSON type selected */}
{[activeJsonType].map((type) => (
Copy link
Collaborator

@gribnoysup gribnoysup Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit hard to understand the scope of work from the ticket. Is that correct that there's actually no choice here? Is this work planned for later? If there's a follow-up work planned, we usually use // TODO(ticket number): description comments in the code to annotate that, it's low effort and should make reviewing for us easier

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a //TODO comment with a description. We're planning to show dropdown options in a follow up ticket - CLOUDP-344400. They will be derived from a hard-coded map of JSON types to faker methods.

<Option key={type} value={type}>
{type}
</Option>
))}
</Select>
<Select
label="Faker Function"
allowDeselect={false}
value={activeFakerFunction}
onChange={onFakerFunctionSelect}
>
{/* TODO(CLOUDP-344400): Make the select input editable and render other JSON types */}
{[activeFakerFunction].map((field) => (
<Option key={field} value={field}>
{field}
</Option>
))}
</Select>
{activeFakerFunction === UNRECOGNIZED_FAKER_METHOD && (
<Banner variant={BannerVariant.Warning}>
Please select a function or we will default fill this field with the
string &quot;Unrecognized&quot;
</Banner>
)}
{/* TODO(CLOUDP-344400): Render faker function parameters once we have a way to validate them. */}
</div>
);
};

export default FakerMappingSelector;
Original file line number Diff line number Diff line change
@@ -1,10 +1,172 @@
import {
Body,
Button,
ButtonSize,
ButtonVariant,
css,
Link,
palette,
spacing,
SpinLoaderWithLabel,
} from '@mongodb-js/compass-components';
import React from 'react';
import FieldSelector from './schema-field-selector';
import FakerMappingSelector from './faker-mapping-selector';
import type { FakerSchemaMapping, MockDataGeneratorState } from './types';

// TODO: More to come from CLOUDP-333853, CLOUDP-333854
const FakerSchemaEditorScreen = () => {
const containerStyles = css({
display: 'flex',
flexDirection: 'column',
gap: spacing[400],
});

const innerEditorStyles = css({
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
});

const titleStyles = css({
color: palette.black,
fontWeight: 600,
fontSize: '16px',
lineHeight: '20px',
marginBottom: 0,
});

const bodyStyles = css({
color: palette.gray.dark1,
});

const confirmMappingsButtonStyles = css({
width: '200px',
});

const schemaEditorLoaderStyles = css({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
});

const FakerSchemaEditorContent = ({
fakerSchemaMappings,
onSchemaConfirmed,
}: {
fakerSchemaMappings: FakerSchemaMapping[];
onSchemaConfirmed: (isConfirmed: boolean) => void;
}) => {
const [fakerSchemaFormValues, setFakerSchemaFormValues] =
React.useState<Array<FakerSchemaMapping>>(fakerSchemaMappings);
const [activeField, setActiveField] = React.useState<string>(
fakerSchemaFormValues[0].fieldPath
);

const activeJsonType = fakerSchemaFormValues.find(
(mapping) => mapping.fieldPath === activeField
)?.mongoType;
const activeFakerFunction = fakerSchemaFormValues.find(
(mapping) => mapping.fieldPath === activeField
)?.fakerMethod;

const resetIsSchemaConfirmed = () => {
onSchemaConfirmed(false);
};

const onJsonTypeSelect = (newJsonType: string) => {
const updatedFakerFieldMapping = fakerSchemaFormValues.find(
(mapping) => mapping.fieldPath === activeField
);
if (updatedFakerFieldMapping) {
updatedFakerFieldMapping.mongoType = newJsonType;
setFakerSchemaFormValues(
fakerSchemaFormValues.map((mapping) =>
mapping.fieldPath === activeField ? updatedFakerFieldMapping : mapping
)
);
resetIsSchemaConfirmed();
}
};

const onFakerFunctionSelect = (newFakerFunction: string) => {
const updatedFakerFieldMapping = fakerSchemaFormValues.find(
(mapping) => mapping.fieldPath === activeField
);
if (updatedFakerFieldMapping) {
updatedFakerFieldMapping.fakerMethod = newFakerFunction;
setFakerSchemaFormValues(
fakerSchemaFormValues.map((mapping) =>
mapping.fieldPath === activeField ? updatedFakerFieldMapping : mapping
)
);
resetIsSchemaConfirmed();
}
};

return (
<>
<div className={innerEditorStyles}>
<FieldSelector
activeField={activeField}
fields={fakerSchemaFormValues.map((mapping) => mapping.fieldPath)}
onFieldSelect={setActiveField}
/>
{activeJsonType && activeFakerFunction && (
<FakerMappingSelector
activeJsonType={activeJsonType}
activeFakerFunction={activeFakerFunction}
onJsonTypeSelect={onJsonTypeSelect}
onFakerFunctionSelect={onFakerFunctionSelect}
/>
)}
</div>
<Button
size={ButtonSize.Small}
className={confirmMappingsButtonStyles}
variant={ButtonVariant.Primary}
onClick={() => onSchemaConfirmed(true)}
>
Confirm mappings
</Button>
</>
);
};

const FakerSchemaEditorScreen = ({
onSchemaConfirmed,
fakerSchemaGenerationState,
}: {
isSchemaConfirmed: boolean;
onSchemaConfirmed: (isConfirmed: boolean) => void;
fakerSchemaGenerationState: MockDataGeneratorState;
}) => {
return (
<div data-testid="faker-schema-editor">
Schema Editor Content Placeholder
<div data-testid="faker-schema-editor" className={containerStyles}>
<div>
<h3 className={titleStyles}>
Confirm Field to Faker Function Mappings
</h3>
<Body className={bodyStyles}>
We have sampled your collection and created a schema based on your
documents. That schema has been sent to an LLM and it has returned the
following mapping between your schema fields and{' '}
<Link href="https://fakerjs.dev/api/faker.html">faker functions</Link>
.
</Body>
</div>
{fakerSchemaGenerationState.status === 'in-progress' && (
<div
data-testid="faker-schema-editor-loader"
className={schemaEditorLoaderStyles}
>
<SpinLoaderWithLabel progressText="Processing Documents..." />
</div>
)}
{fakerSchemaGenerationState.status === 'completed' && (
<FakerSchemaEditorContent
fakerSchemaMappings={fakerSchemaGenerationState.fakerSchema}
onSchemaConfirmed={onSchemaConfirmed}
/>
)}
</div>
);
};
Expand Down
Loading
Loading