Skip to content

Commit

Permalink
feat(Beans): Port Bean Configuration UI
Browse files Browse the repository at this point in the history
Fixes: #8
  • Loading branch information
igarashitm committed Sep 25, 2023
1 parent 9cf8c4d commit d59e459
Show file tree
Hide file tree
Showing 22 changed files with 1,696 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"eslint-plugin-react-refresh": "^0.4.3",
"jest": "^29.4.2",
"jest-environment-jsdom": "^29.4.2",
"lodash": "^4.17.21",
"prettier": "^3.0.0",
"react-test-renderer": "^18.2.0",
"sass": "^1.63.6",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { AddPropertyButtons } from './AddPropertyButtons';
import { screen } from '@testing-library/dom';
import { fireEvent, render } from '@testing-library/react';

describe('AddPropertyButtons.tsx', () => {
test('Add string property button', () => {
const events: boolean[] = [];
render(
<AddPropertyButtons
path={['foo', 'bar']}
createPlaceholder={(isObject) => events.push(isObject)}
/>,
);
const element = screen.getByTestId('properties-add-string-property-foo-bar-btn');
expect(events.length).toBe(0);
fireEvent.click(element);
expect(events.length).toBe(1);
expect(events[0]).toBeFalsy();
});

test('Add object property button', () => {
const events: boolean[] = [];
render(
<AddPropertyButtons
path={['foo', 'bar']}
createPlaceholder={(isObject) => events.push(isObject)}
/>,
);
const element = screen.getByTestId('properties-add-object-property-foo-bar-btn');
expect(events.length).toBe(0);
fireEvent.click(element);
expect(events.length).toBe(1);
expect(events[0]).toBeTruthy();
});
});
52 changes: 52 additions & 0 deletions packages/ui/src/components/MetadataEditor/AddPropertyButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Button, Split, SplitItem, Tooltip } from '@patternfly/react-core';
import { FolderPlusIcon, PlusCircleIcon } from '@patternfly/react-icons';

type AddPropertyPopoverProps = {
showLabel?: boolean;
path: string[];
disabled?: boolean;
createPlaceholder: (isObject: boolean) => void;
};

/**
* A set of "add string property" and "add object property" buttons which triggers creating a placeholder.
* @param props
* @constructor
*/
export function AddPropertyButtons({
showLabel = false,
path,
disabled = false,
createPlaceholder,
}: AddPropertyPopoverProps) {
return (
<Split>
<SplitItem>
<Tooltip content="Add string property">
<Button
data-testid={`properties-add-string-property-${path.join('-')}-btn`}
variant={'link'}
icon={<PlusCircleIcon />}
isDisabled={disabled}
onClick={() => createPlaceholder(false)}
>
{showLabel && 'Add string property'}
</Button>
</Tooltip>
</SplitItem>
<SplitItem>
<Tooltip content="Add object property">
<Button
data-testid={`properties-add-object-property-${path.join('-')}-btn`}
variant={'link'}
icon={<FolderPlusIcon />}
isDisabled={disabled}
onClick={() => createPlaceholder(true)}
>
{showLabel && 'Add object property'}
</Button>
</Tooltip>
</SplitItem>
</Split>
);
}
17 changes: 17 additions & 0 deletions packages/ui/src/components/MetadataEditor/FieldLabelIcon.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { FieldLabelIcon } from './FieldLabelIcon';
import { screen } from '@testing-library/dom';
import { fireEvent, render, waitFor } from '@testing-library/react';

describe('FieldLabelIcon.tsx', () => {
test('component renders if open', async () => {
render(
<FieldLabelIcon disabled={false} />
);
const element = screen.getByTestId('field-label-icon');
expect(element).toBeInTheDocument();
fireEvent.click(element);
await waitFor(() =>
expect(screen.getByTestId('property-description-popover')).toBeInTheDocument(),
);
});
});
49 changes: 49 additions & 0 deletions packages/ui/src/components/MetadataEditor/FieldLabelIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Button, Popover, Text, TextVariants } from '@patternfly/react-core';
import { HelpIcon } from '@patternfly/react-icons';

type FieldLabelIconProps = {
defaultValue?: any;
description?: string;
disabled: boolean;
};

/**
* Returns a label tooltip element for the form or undefined if the field has no description
* @returns
* @param props
*/
export const FieldLabelIcon = (props: FieldLabelIconProps) => {
const headerContent = props.disabled
? 'Please use the source code editor to configure this property.'
: '';
const bodyContent = props.description ? props.description : '';

const footerContent = () => {
return (
<Text component={TextVariants.small}>
Default: {props.defaultValue ?? <i>No default value</i>}
</Text>
);
};

return (
<Popover
aria-label={'Property description'}
headerContent={headerContent}
bodyContent={bodyContent}
data-testid={'property-description-popover'}
footerContent={footerContent}
>
<Button
variant="plain"
type="button"
aria-label="More info for field"
aria-describedby="form-group-label-info"
className="pf-c-form__group-label-help"
data-testid={'field-label-icon'}
>
<HelpIcon />
</Button>
</Popover>
);
};
12 changes: 12 additions & 0 deletions packages/ui/src/components/MetadataEditor/MetadataEditor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.metadataEditorModal {
height: 90vh;
width: 90vw;
}

.metadataEditorModalListView {
width: 50vw;
}

.metadataEditorModalDetailsView {
width: 50vw;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { MetadataEditor } from './MetadataEditor';
import { mockSchema } from './TestUtil';
import { useArgs } from '@storybook/client-api';
import { StoryFn, Meta } from '@storybook/react';

export default {
title: 'Metadata/MetadataEditorModal',
component: MetadataEditor,
excludeStories: ['schemaMock'],
decorators: [
(Story) => (
<div style={{ margin: '3em' }}>
<Story />
</div>
),
],
argTypes: { handleCloseModal: { action: 'clicked' } },
} as Meta<typeof MetadataEditor>;

const Template: StoryFn<typeof MetadataEditor> = (args) => {
const [{ isModalOpen }, updateArgs] = useArgs();
return (
<>
<button onClick={() => updateArgs({ isModalOpen: !isModalOpen })}>
Open Metadata Editor Modal
</button>
<MetadataEditor {...args} />
</>
);
};

export const BeansArray = Template.bind({});
BeansArray.args = {
name: 'beans',
schema: mockSchema.beans,
};

export const SingleObject = Template.bind({});
SingleObject.args = {
name: 'singleObject',
schema: mockSchema.single,
};
Loading

0 comments on commit d59e459

Please sign in to comment.