-
Notifications
You must be signed in to change notification settings - Fork 134
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: InspectorFields component (#1547)
* Feat: InspectorFields component * Feat: getControlFields() * Refactor: Fix getControlFields find call. * Lint: Eslint fixes * Test: Add Unit tests for InspectorFields. Co-authored-by: Teresa Gobble <[email protected]> * Refactor: (tests) Cleaning up InspectorFields.test * Update InspectorFields.tsx * CI/CD: Fix audit vulnerability. --------- Co-authored-by: Teresa Gobble <[email protected]>
- Loading branch information
1 parent
bfdf19b
commit 0fb5e9e
Showing
9 changed files
with
1,702 additions
and
970 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
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
26 changes: 26 additions & 0 deletions
26
packages/block-editor-utils/src/components/EditFormFields.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,26 @@ | ||
import * as React from 'react'; | ||
import { BlockEditProps } from '@wordpress/blocks'; | ||
import InspectorFields from './InspectorFields.js'; | ||
import { Field } from '../types/index.js'; | ||
|
||
interface EditFormFieldsProps<T extends Record<string, any>> { | ||
props: BlockEditProps<T>; | ||
fields: Field[]; | ||
} | ||
|
||
function EditFormFields<T extends Record<string, any>>({ | ||
props, | ||
fields, | ||
}: EditFormFieldsProps<T>) { | ||
const inspectorFields = fields.filter( | ||
(field: Field) => field.location === 'inspector', | ||
); | ||
return ( | ||
<> | ||
<InspectorFields fields={inspectorFields} props={props} /> | ||
<div>Edit mode</div> | ||
</> | ||
); | ||
} | ||
|
||
export default EditFormFields; |
43 changes: 43 additions & 0 deletions
43
packages/block-editor-utils/src/components/InspectorFields.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,43 @@ | ||
import * as React from 'react'; | ||
import { InspectorControls } from '@wordpress/block-editor'; | ||
import { PanelBody } from '@wordpress/components'; | ||
import { BlockEditProps } from '@wordpress/blocks'; | ||
import { applyFilters } from '@wordpress/hooks'; | ||
import { Control, Field } from '../types/index.js'; | ||
|
||
interface InspectorFieldsProps<T extends Record<string, any>> { | ||
fields: Field[]; | ||
props: BlockEditProps<T>; | ||
} | ||
|
||
function InspectorFields<T extends Record<string, any>>({ | ||
fields, | ||
props, | ||
}: InspectorFieldsProps<T>) { | ||
const loadedControls = applyFilters('faustBlockEditorUtils.controls', {}) as { | ||
[key: string]: Control; | ||
}; | ||
return ( | ||
<InspectorControls key="FaustBlockInspectorControls"> | ||
<> | ||
{/* eslint-disable-next-line @typescript-eslint/no-unsafe-call */} | ||
{fields.map((field: Field) => { | ||
const ControlField = loadedControls[field.control]; | ||
if (!ControlField) { | ||
return null; | ||
} | ||
return ( | ||
<PanelBody | ||
className="faust-inspector-form-field" | ||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions | ||
key={`inspector-controls-panel-${field.name}`}> | ||
<ControlField config={field} props={props} /> | ||
</PanelBody> | ||
); | ||
})} | ||
</> | ||
</InspectorControls> | ||
); | ||
} | ||
|
||
export default InspectorFields; |
56 changes: 56 additions & 0 deletions
56
packages/block-editor-utils/src/helpers/getControlFields.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,56 @@ | ||
import { BlockConfiguration } from '@wordpress/blocks'; | ||
import { Field, FieldControl, FieldType } from '../types/index.js'; | ||
|
||
const blockAttributeTypeToControlMap: Record<FieldType, FieldControl> = { | ||
string: 'text', | ||
boolean: 'radio', | ||
integer: 'number', | ||
number: 'number', | ||
object: 'textarea', | ||
array: 'textarea', | ||
}; | ||
|
||
/** | ||
* Returns a list of Field objects that describe how the Component Editor Fields configuration. | ||
* Uses both the Block.json and the blocks editorFields config to create the final list. | ||
* The logic is explained in detail in the RFC document for React Components To Blocks. | ||
* | ||
* @param blockJson Block.json object | ||
* @param editorFields Block config editorFields metadata | ||
* @returns | ||
*/ | ||
function getControlFields( | ||
blockJson: BlockConfiguration, | ||
editorFields: Partial<Field>[], | ||
): Field[] { | ||
const fields: Field[] = []; | ||
Object.entries(blockJson.attributes).forEach(([key, value]) => { | ||
const fieldConfig = Object.entries(editorFields).find(([name]) => { | ||
return key === name; | ||
})?.[1]; | ||
const fieldType: FieldType = (value as any).type; | ||
const control = blockAttributeTypeToControlMap[fieldType] ?? 'text'; | ||
// Set default field by merging both blockAttributes meta and editorFields hints. | ||
if (fieldConfig) { | ||
fields.push({ | ||
name: key, | ||
label: fieldConfig.label ?? key, | ||
type: fieldType, | ||
location: fieldConfig.location ?? 'editor', | ||
control: fieldConfig?.control ?? control, | ||
}); | ||
} else { | ||
// Set default field by using only blockAttributes meta | ||
fields.push({ | ||
name: key, | ||
label: key, | ||
type: fieldType, | ||
location: 'editor', | ||
control, | ||
}); | ||
} | ||
}); | ||
return fields; | ||
} | ||
|
||
export default getControlFields; |
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
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
97 changes: 97 additions & 0 deletions
97
packages/block-editor-utils/tests/components/InspectorFields.test.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,97 @@ | ||
import * as React from 'react'; | ||
import { render, screen } from '@testing-library/react'; | ||
import InspectorFields from '../../src/components/InspectorFields.js'; | ||
import { actions, filters, addFilter } from '@wordpress/hooks'; | ||
import { Control, Field } from '../../src/types/index.js'; | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
jest.mock('@wordpress/block-editor', () => { | ||
const originalModule = jest.requireActual('@wordpress/block-editor'); | ||
return { | ||
...originalModule, | ||
InspectorControls: jest.fn((props) => ( | ||
<div data-testid="inspector-controls-test">{props.children}</div> | ||
)), | ||
}; | ||
}); | ||
jest.mock('@wordpress/components', () => { | ||
const originalModule = jest.requireActual('@wordpress/components'); | ||
return { | ||
...originalModule, | ||
PanelBody: jest.fn((props) => ( | ||
<div data-testid="panel-body-test">{props.children}</div> | ||
)), | ||
}; | ||
}); | ||
|
||
beforeEach(() => { | ||
[actions, filters].forEach((hooks) => { | ||
for (const k in hooks) { | ||
if ('__current' === k) { | ||
continue; | ||
} | ||
|
||
delete hooks[k]; | ||
} | ||
delete hooks.all; | ||
}); | ||
}); | ||
|
||
function filterA(controls: { [key: string]: Control }) { | ||
// eslint-disable-next-line no-param-reassign | ||
controls.color = () => <div>Another Color</div>; | ||
return controls; | ||
} | ||
|
||
const blockProps = { | ||
clientId: '1', | ||
setAttributes: () => null, | ||
context: {}, | ||
attributes: { | ||
message: 'Hello', | ||
}, | ||
isSelected: false, | ||
className: 'SimpleBlock', | ||
}; | ||
|
||
describe('<InspectorFields />', () => { | ||
it('renders an empty InspectorFields if no fields are provided', () => { | ||
const fields: Field[] = []; | ||
addFilter('faustBlockEditorUtils.controls', 'my_callback', filterA); | ||
render(<InspectorFields fields={fields} props={blockProps} />); | ||
expect(screen.getByTestId('inspector-controls-test')) | ||
.toMatchInlineSnapshot(` | ||
<div | ||
data-testid="inspector-controls-test" | ||
/> | ||
`); | ||
}); | ||
it('renders InspectorFields if matching fields are provided', () => { | ||
const fields: Field[] = [ | ||
{ | ||
type: 'string', | ||
control: 'color', | ||
name: 'myColor', | ||
location: 'inspector', | ||
}, | ||
{ | ||
type: 'string', | ||
control: 'text', | ||
name: 'myText', | ||
location: 'inspector', | ||
}, | ||
]; | ||
addFilter('faustBlockEditorUtils.controls', 'my_callback', filterA); | ||
render(<InspectorFields fields={fields} props={blockProps} />); | ||
expect(screen.getAllByText('Another Color')).toMatchInlineSnapshot(` | ||
[ | ||
<div> | ||
Another Color | ||
</div>, | ||
] | ||
`); | ||
}); | ||
}); |
0fb5e9e
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.
Check out the recent updates to your Atlas environment:
Learn more about building on Atlas in our documentation.