Components
Layout components are loaded by the Layout Manager.
New components should be added to the componentMap
object. These keys are automatically chunked and lazy loaded keeping the initial bundle size down.
export const componentMap = {
...
mycomponent: './MyComponent'
}
This component is an intermediate wrapper which validates the yaml before loading the actual component:
import type { YamlComponent } from '@esrf/daiquiri-lib';
import { YamlAsserts } from '@esrf/daiquiri-lib';
import MyComponent from 'components/MyComponent';
export default function Yaml(props: YamlComponent) {
const { providers, yamlNode, option1, ...unknownOptions } = props;
YamlAsserts.assertString(yamlNode, 'option1', option1);
YamlAsserts.assertNoUnknownKeys(yamlNode, unknownOptions);
return <MyComponent option1={option1} />;
}
And the example layout yaml that would load this component:
- type: component
component: mycomponent
title: A component
option1: value
Building a Component
Components should be placed into src/components/<component>.tsx
and under a subdirectory if required. The tsx
extension indicates this is a React component and is enforced by eslint-config-galex.
New components should be written as React functional components using typescript, in their simplest form:
interface Props {
options: Record<string, any>,
...
}
export default function MyComponent(props: Props) {
const { options, ...rest } = props
return (
...
)
}
Any options passed from the layout yaml will be received into options
. Pass additional props into the component from the store by connecting (see next section).
Connecting
Components need to be connected to the store. To provide clear separation between this and the component the redux connect method is placed in its own file. This makes the components mostly independent.
Connect files should be placed into src/connect/<component>.ts
with the same name as the component they are connecting. Connect files use the ts
extension as they are not a React component.
import { connect } from 'react-redux';
import { withNamespace } from 'providers/namespace';
import hardware from 'providers/hardware';
import MyComponent from 'components/MyComponent';
const mapStateToProps = (state: any, own: any) => ({
hardwareObjects: own.providers.hardware.hardware.selector('results', state),
});
const mapDispatchToProps = (dispatch: any, own: any) => ({
actions: {
fetchHardware: () => own.providers.hardware.hardware.fetch(),
},
});
export default withNamespace({ hardware })(
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
);
The withNamespace HOC provides a way to interact easily with namespaced parts of the redux store corresponding to asynchronous responses from a REST API. For more details on providers see redux-provider.
With the above connect file the component MyComponent
would recieve hardwareObjects
and actions.fetchHardware
into its props
, for example:
{
hardwareObjects: {
...
},
actions: {
fetchHardware: func
},
options: {
...
}
}
Helpers
Daiquiri UI provides a wealth of components to speed up application development.
Some of these include:
- Tables / Cells
- ModalDialog / ButtonTriggerModalStore
- Forms / Inline editing
- FullSizer
- CanvasEnhancer
Documentation
An mdx
documentation should be created for each new component for docusaurus under the ui-components
folder. The <DaiquiriCode>
helper can be used to render the component in a mockup with the relevent daiquiri sass styles injected and isolated. Mocked json props / data can be placed in src/components/mocks
.
import DaiquiriCode from '@site/src/DaiquiriCode';
import MyComponent from 'daiquiri-ui/src/components/MyComponent';
import props from 'daiquiri-ui/src/components/mocks/MyComponent.json';
<DaiquiriCode>
<MyComponent {...props} />;
</DaiquiriCode>;