-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(24318): Create a compact editable table for attribute/value editor * refactor(24318): add mapping for custom fields * feat(24318): add custom editable table for user properties * feat(24143): add custom editor for compact property/value items * fix(24143): fix JSX * feat(24143): add support for add and delete item * refactor(24143): refactor editable cell and add required checks * refactor(24143): refactor initial state * refactor(24143): fix publish * feat(24143): add ui mapping to adapters * refactor(24318): update dependencies * refactor(24318): refactor the form widget to export the UI components * refactor(24318): refactor the table component * refactor(24318): add a grid component * refactor(24318): revert datagrid trials * feat(24318): add custom templates for the compact grid * refactor(24318): refactor compact grid field * feat(24318): add custom field to the configuration of the adapter editor * test(24318): add mocks and tests
- Loading branch information
Showing
17 changed files
with
471 additions
and
2 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
hivemq-edge/src/frontend/src/__test-utils__/rjsf/rjsf.mocks.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { FC, FormEvent } from 'react' | ||
import validator from '@rjsf/validator-ajv8' | ||
import { RJSFSchema, UiSchema, RJSFValidationError, RegistryFieldsType } from '@rjsf/utils' | ||
import Form from '@rjsf/chakra-ui' | ||
import { IChangeEvent } from '@rjsf/core' | ||
|
||
import { ObjectFieldTemplate } from '@/components/rjsf/ObjectFieldTemplate.tsx' | ||
import { FieldTemplate } from '@/components/rjsf/FieldTemplate.tsx' | ||
import { BaseInputTemplate } from '@/components/rjsf/BaseInputTemplate.tsx' | ||
import { ArrayFieldTemplate } from '@/components/rjsf/ArrayFieldTemplate.tsx' | ||
import { ArrayFieldItemTemplate } from '@/components/rjsf/ArrayFieldItemTemplate.tsx' | ||
|
||
interface RjsfMocksProps { | ||
schema: RJSFSchema | ||
uiSchema?: UiSchema | undefined | ||
onSubmit?: (data: IChangeEvent, event: FormEvent) => void | ||
onError?: (errors: RJSFValidationError[]) => void | ||
formData?: unknown | ||
fields?: RegistryFieldsType | ||
} | ||
|
||
const RjsfMocks: FC<RjsfMocksProps> = ({ schema, uiSchema, formData, fields, onSubmit, onError }) => { | ||
return ( | ||
<Form | ||
id="mock-jsonschema-form" | ||
schema={schema} | ||
uiSchema={uiSchema} | ||
templates={{ | ||
ObjectFieldTemplate, | ||
FieldTemplate, | ||
BaseInputTemplate, | ||
ArrayFieldTemplate, | ||
ArrayFieldItemTemplate, | ||
}} | ||
liveValidate | ||
showErrorList="bottom" | ||
validator={validator} | ||
onSubmit={onSubmit} | ||
onError={onError} | ||
formData={formData} | ||
fields={fields} | ||
/> | ||
) | ||
} | ||
|
||
export default RjsfMocks |
124 changes: 124 additions & 0 deletions
124
hivemq-edge/src/frontend/src/components/rjsf/Fields/CompactArrayField.spec.cy.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/// <reference types="cypress" /> | ||
import { RJSFSchema, UiSchema } from '@rjsf/utils' | ||
|
||
import RjsfMocks from '@/__test-utils__/rjsf/rjsf.mocks.tsx' | ||
import { adapterJSFFields } from '@/modules/ProtocolAdapters/utils/uiSchema.utils.ts' | ||
|
||
const MOCK_SCHEMA: RJSFSchema = { | ||
title: 'User Properties', | ||
description: 'Arbitrary properties to associate with the subscription', | ||
maxItems: 3, | ||
type: 'array', | ||
items: { | ||
type: 'object', | ||
properties: { | ||
name: { | ||
type: 'string', | ||
title: 'Property Name', | ||
description: 'Name of the associated property', | ||
}, | ||
value: { | ||
type: 'string', | ||
title: 'Property Value', | ||
description: 'Value of the associated property', | ||
}, | ||
}, | ||
required: ['name', 'value'], | ||
}, | ||
} | ||
|
||
const MOCK_UI_SCHEMA: UiSchema = { | ||
'ui:field': 'compactTable', | ||
} | ||
|
||
const MOCK_DATA = [ | ||
{ | ||
name: 'name1', | ||
value: '1', | ||
}, | ||
{ | ||
name: 'name2', | ||
value: 'value', | ||
}, | ||
] | ||
|
||
describe('CompactArrayField', () => { | ||
beforeEach(() => { | ||
cy.viewport(800, 900) | ||
}) | ||
|
||
it('should render the compact table ', () => { | ||
const onSubmit = cy.stub().as('onSubmit') | ||
cy.mountWithProviders( | ||
<RjsfMocks | ||
schema={MOCK_SCHEMA} | ||
uiSchema={MOCK_UI_SCHEMA} | ||
formData={MOCK_DATA} | ||
onSubmit={onSubmit} | ||
fields={adapterJSFFields} | ||
/> | ||
) | ||
|
||
cy.get('[role="group"] h5').should('contain.text', 'User Properties') | ||
cy.get('[role="group"] p').should('contain.text', 'Arbitrary properties to associate with the subscription') | ||
cy.get('table thead tr th').should('have.length', 3) | ||
cy.get('table thead tr th').eq(0).should('contain.text', 'Property Name') | ||
cy.get('table thead tr th').eq(1).should('contain.text', 'Property Value') | ||
cy.get('table thead tr th').eq(2).should('contain.text', 'Actions') | ||
|
||
cy.get('table tbody tr').should('have.length', 2) | ||
cy.get('table tbody td input').should('have.length', 4) | ||
cy.get('table tbody td input').eq(0).should('have.value', 'name1') | ||
cy.get('table tbody td input').eq(1).should('have.value', '1') | ||
cy.get('table tbody td input').eq(2).should('have.value', 'name2') | ||
cy.get('table tbody td input').eq(3).should('have.value', 'value') | ||
|
||
cy.get("button[type='submit']").click() | ||
cy.get('@onSubmit').should( | ||
'have.been.calledWith', | ||
Cypress.sinon.match({ | ||
formData: MOCK_DATA, | ||
}) | ||
) | ||
}) | ||
|
||
it('should add and remove items', () => { | ||
cy.mountWithProviders( | ||
<RjsfMocks schema={MOCK_SCHEMA} uiSchema={MOCK_UI_SCHEMA} formData={MOCK_DATA} fields={adapterJSFFields} /> | ||
) | ||
|
||
cy.get('table tbody tr').should('have.length', 2) | ||
cy.getByTestId('compact-add-item').click() | ||
cy.get('table tbody tr').should('have.length', 3) | ||
cy.get('table tbody td input') | ||
.eq(4) | ||
.should('have.value', '') | ||
.should('have.attr', 'required', 'required') | ||
.should('have.attr', 'aria-invalid', 'true') | ||
cy.get('[role="alert"] ul li').should('have.length', 2) | ||
|
||
cy.getByTestId('compact-delete-item').eq(2).click() | ||
cy.get('table tbody tr').should('have.length', 2) | ||
}) | ||
|
||
it('should be accessible ', () => { | ||
cy.injectAxe() | ||
|
||
cy.mountWithProviders( | ||
<RjsfMocks | ||
schema={MOCK_SCHEMA} | ||
uiSchema={MOCK_UI_SCHEMA} | ||
formData={MOCK_DATA} | ||
onSubmit={(e) => console.log(e)} | ||
fields={adapterJSFFields} | ||
/> | ||
) | ||
|
||
cy.checkAccessibility(undefined, { | ||
rules: { | ||
// h5 used for sections is not in order. Not detected on other tests | ||
'heading-order': { enabled: false }, | ||
}, | ||
}) | ||
}) | ||
}) |
27 changes: 27 additions & 0 deletions
27
hivemq-edge/src/frontend/src/components/rjsf/Fields/CompactArrayField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { FC } from 'react' | ||
import { FieldProps } from '@rjsf/utils' | ||
import { RJSFSchema } from '@rjsf/utils/src/types.ts' | ||
|
||
import { AdapterContext } from '@/modules/ProtocolAdapters/types.ts' | ||
import { CompactArrayFieldTemplate } from '@/components/rjsf/Templates/CompactArrayFieldTemplate.tsx' | ||
import { CompactFieldTemplate } from '@/components/rjsf/Templates/CompactFieldTemplate.tsx' | ||
import { CompactBaseInputTemplate } from '@/components/rjsf/Templates/CompactBaseInputTemplate.tsx' | ||
import { CompactObjectFieldTemplate } from '@/components/rjsf/Templates/CompactObjectFieldTemplate.tsx' | ||
import { CompactArrayFieldItemTemplate } from '@/components/rjsf/Templates/CompactArrayFieldItemTemplate.tsx' | ||
|
||
const CompactArrayField: FC<FieldProps<unknown, RJSFSchema, AdapterContext>> = (props) => { | ||
const { registry } = props | ||
|
||
registry.templates = { | ||
...registry.templates, | ||
ArrayFieldTemplate: CompactArrayFieldTemplate, | ||
ArrayFieldItemTemplate: CompactArrayFieldItemTemplate, | ||
FieldTemplate: CompactFieldTemplate, | ||
BaseInputTemplate: CompactBaseInputTemplate, | ||
ObjectFieldTemplate: CompactObjectFieldTemplate, | ||
} | ||
|
||
return <props.registry.fields.ArrayField {...props} /> | ||
} | ||
|
||
export default CompactArrayField |
18 changes: 18 additions & 0 deletions
18
hivemq-edge/src/frontend/src/components/rjsf/Fields/types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { JSONSchema7 } from 'json-schema' | ||
import { Dispatch, SetStateAction } from 'react' | ||
|
||
export type CustomPropertyValue = string | ||
export type FormDataItem = Record<string, CustomPropertyValue> | ||
export type CustomPropertyForm = FormDataItem[] | ||
|
||
export interface DataGridProps { | ||
data: CustomPropertyForm | ||
columnTypes: [string, JSONSchema7][] | ||
isDisabled?: boolean | ||
required?: string[] | ||
maxItems?: number | ||
onHandleDeleteItem?: (index: number) => void | ||
onHandleAddItem?: () => void | ||
onUpdateData?: (rowIndex: number, columnId: string, value: CustomPropertyValue) => void | ||
onSetData?: Dispatch<SetStateAction<CustomPropertyForm>> | ||
} |
73 changes: 73 additions & 0 deletions
73
hivemq-edge/src/frontend/src/components/rjsf/Templates/CompactArrayFieldItemTemplate.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { FC, useMemo } from 'react' | ||
import { ArrayFieldTemplateItemType } from '@rjsf/utils' | ||
import { ButtonGroup, Td, Tr } from '@chakra-ui/react' | ||
import { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } from '@/components/rjsf/__internals/IconButton.tsx' | ||
|
||
export const CompactArrayFieldItemTemplate: FC<ArrayFieldTemplateItemType> = (props) => { | ||
const { | ||
children, | ||
index, | ||
hasMoveUp, | ||
hasRemove, | ||
hasMoveDown, | ||
hasCopy, | ||
disabled, | ||
readonly, | ||
onCopyIndexClick, | ||
onDropIndexClick, | ||
onReorderClick, | ||
uiSchema, | ||
registry, | ||
} = props | ||
|
||
const onCopyClick = useMemo(() => onCopyIndexClick(index), [index, onCopyIndexClick]) | ||
const onRemoveClick = useMemo(() => onDropIndexClick(index), [index, onDropIndexClick]) | ||
const onArrowUpClick = useMemo(() => onReorderClick(index, index - 1), [index, onReorderClick]) | ||
const onArrowDownClick = useMemo(() => onReorderClick(index, index + 1), [index, onReorderClick]) | ||
|
||
return ( | ||
<Tr> | ||
{children} | ||
<Td> | ||
<ButtonGroup isAttached size="sm" orientation="horizontal" ml={2}> | ||
{(hasMoveUp || hasMoveDown) && ( | ||
<MoveUpButton | ||
data-testid="compact-up-item" | ||
disabled={disabled || readonly || !hasMoveUp} | ||
onClick={onArrowUpClick} | ||
uiSchema={uiSchema} | ||
registry={registry} | ||
/> | ||
)} | ||
{(hasMoveUp || hasMoveDown) && ( | ||
<MoveDownButton | ||
data-testid="compact-down-item" | ||
disabled={disabled || readonly || !hasMoveDown} | ||
onClick={onArrowDownClick} | ||
uiSchema={uiSchema} | ||
registry={registry} | ||
/> | ||
)} | ||
{hasCopy && ( | ||
<CopyButton | ||
data-testid="compact-copy-item" | ||
disabled={disabled || readonly} | ||
onClick={onCopyClick} | ||
uiSchema={uiSchema} | ||
registry={registry} | ||
/> | ||
)} | ||
{hasRemove && ( | ||
<RemoveButton | ||
data-testid="compact-delete-item" | ||
disabled={disabled || readonly} | ||
onClick={onRemoveClick} | ||
uiSchema={uiSchema} | ||
registry={registry} | ||
/> | ||
)} | ||
</ButtonGroup> | ||
</Td> | ||
</Tr> | ||
) | ||
} |
76 changes: 76 additions & 0 deletions
76
hivemq-edge/src/frontend/src/components/rjsf/Templates/CompactArrayFieldTemplate.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { FC } from 'react' | ||
import { JSONSchema7 } from 'json-schema' | ||
import { RJSFSchema, ArrayFieldTemplateProps, getTemplate, getUiOptions, ArrayFieldTemplateItemType } from '@rjsf/utils' | ||
import { Box, HStack, Table, Tbody, Th, Thead, Tr } from '@chakra-ui/react' | ||
|
||
import AddButton from '@/components/rjsf/__internals/AddButton.tsx' | ||
import { AdapterContext } from '@/modules/ProtocolAdapters/types.ts' | ||
|
||
export const CompactArrayFieldTemplate: FC<ArrayFieldTemplateProps<unknown, RJSFSchema, AdapterContext>> = (props) => { | ||
const { canAdd, disabled, idSchema, uiSchema, items, onAddClick, readonly, registry, required, schema, title } = props | ||
const uiOptions = getUiOptions(uiSchema) | ||
const ArrayFieldDescriptionTemplate = getTemplate<'ArrayFieldDescriptionTemplate'>( | ||
'ArrayFieldDescriptionTemplate', | ||
registry, | ||
uiOptions | ||
) | ||
const ArrayFieldItemTemplate = getTemplate<'ArrayFieldItemTemplate'>('ArrayFieldItemTemplate', registry, uiOptions) | ||
const ArrayFieldTitleTemplate = getTemplate<'ArrayFieldTitleTemplate'>('ArrayFieldTitleTemplate', registry, uiOptions) | ||
|
||
const { items: SchemaItems } = schema as JSONSchema7 | ||
const { properties } = SchemaItems as JSONSchema7 | ||
|
||
// Better approach to unidentified headers? | ||
if (!properties) return null | ||
|
||
return ( | ||
<Box> | ||
<ArrayFieldTitleTemplate | ||
idSchema={idSchema} | ||
title={uiOptions.title || title} | ||
schema={schema} | ||
uiSchema={uiSchema} | ||
required={required} | ||
registry={registry} | ||
/> | ||
<ArrayFieldDescriptionTemplate | ||
idSchema={idSchema} | ||
description={uiOptions.description || schema.description} | ||
schema={schema} | ||
uiSchema={uiSchema} | ||
registry={registry} | ||
/> | ||
<> | ||
<Table size="xs"> | ||
<Thead> | ||
<Tr> | ||
{Object.entries(properties).map(([key, values]) => { | ||
const { title } = values as JSONSchema7 | ||
return <Th key={key}>{title}</Th> | ||
})} | ||
<Th>Actions</Th> | ||
</Tr> | ||
</Thead> | ||
<Tbody> | ||
{items?.length > 0 && | ||
items?.map(({ key, ...itemProps }: ArrayFieldTemplateItemType) => { | ||
return <ArrayFieldItemTemplate key={key} {...itemProps} /> | ||
})} | ||
</Tbody> | ||
</Table> | ||
{canAdd && ( | ||
<HStack justifyContent="end" mt={2}> | ||
<AddButton | ||
data-testid="compact-add-item" | ||
className="array-item-add" | ||
onClick={onAddClick} | ||
disabled={disabled || readonly} | ||
uiSchema={uiSchema} | ||
registry={registry} | ||
/> | ||
</HStack> | ||
)} | ||
</> | ||
</Box> | ||
) | ||
} |
Oops, something went wrong.