Skip to main content

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.

src/components/yaml-layout/components/componentMap.tsx
export const componentMap = {
...
mycomponent: './MyComponent'
}

This component is an intermediate wrapper which validates the yaml before loading the actual component:

src/components/yaml-layout/components/MyComponent.tsx
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:

layout.yml
- 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:

src/components/MyComponent.tsx
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.

src/connect/MyComponent.ts
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.

docasaurus/docs/ui-components/MyComponent.mdx
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>;