- Dalberg Data Insights React Components Library
- Consuming packages
- Developer TODO and Roadmap
- Core Library
- Components
- Layouts
- Contribution
- In your project, create .npmrc
registry=https://registry.npmjs.org
@dalbergdatainsights:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=<your auth token>
always-auth=true
- Get your GitHub token and plug it in
- Add .npmrc to your .gitignore, don't push your token!
- come up with better props drill ideas
- decide on the GridLayout directly into the container / new component
- decide on the container defaults locations
- Wrapper component still rerenders => research React docs for causes
- Hover and focus application is problematic
- Integrating tailwind in the framework would be beneficial, but requires className merge tech
- Do we need context consume in Grid if it's used on top level only?
- Make it take in breakpoints - find usecase first
- Move component logic separately
- Add sort
- Add highlight
An easier way of creating and assigning common events to components. Events are then packed in a single function that is subscribing to a certain event type.
Common events currently include:
- click: onClick -> usually sets "click" state
- change: onChange -> triggers when active state changes (for dropdown)
- move: onMove -> usually sets point state
- search: onSearch -> triggers whenever the search value changes (for dropdown)
- leave: onLeave -> triggers when the mouse leaves a component
- load: onLoad -> triggers when the object is fully rendered for the first time. For Map, Chart, and other components with loading animations: fires when object is first rendered rather than after the animation. For Dropdown components: fires when dropdown button is first loaded, does not fire during or after input selection.
To register your own event, pass a list of subscribers in the prop of any component.
<Component
{...props}
subscribers={[
{
// fire on click
on: "click",
// console.log the click event (will contain click target)
func: (event) => {
console.log(event)
},
},
]}
/>
State events are triggered when a dependency is changing instead of when a common event is occurring. You can pass any external or internal prop. The powerful difference between the state observers is that instead of an event, the event handler can request arguments from within the component without hoarding states in the parent component.
// example from map click handler
<Component
{...props}
observers={[
{
// fire when "click" state of the component changes
prop: "click",
// request internal props to be passed as args to the handler
args: ["controller", "click", "bounds", "data"],
func: (args) => {
// click was requested above
// don't forget to avoid unnecessary rerendering
if (args.click) {
const newBound = getBound(args.click?.feature?.geometry.coordinates)
const noClickRegions = args.controller.allLabels.filter(
(l) => l !== args.click.name
)
// you can even set new properties to states!
args.click["noClick"] = noClickRegions
// and access functions that would usually be enabled only with refs!
args.controller?.fitBounds(newBound)
} else {
args.controller?.fitBounds(args.bounds)
}
},
},
]}
/>
Every component is initialized with an init function that is executed in the body of a wrapper component before events and states are registered. With a list of utility methods, you can overwrite an init function by either importing the component directly or passing an init prop in the body of the component.
<Component init={(subs, obs, props) => { ...custom logic here}}/>
Most of the interactions, however, can be achieved with props passthrough, config context and event management system.
You can pass the "props" prop to any component. Each component states which props are passed through. Usually, component hierarchy consists of a container - a div in a wrapper component that contains component metadata and initializes event and state listeners and a component-specific structure that starts with a same name component frame (e.g. table for Table, map for Map) and children components where applicable (e.g. header, row, cell for Table, option for Switch etc)
Every component comes with default styling. The user can overwrite entire styling sheets by passing the config in the context in a format of
{
ComponentName: {
// normal props
props: {
// props passthrough
}
}
OtherComponentName: {...}
}
Alternatively, each component CSS can be overwritten by passing props separately. Default props are overwritten by context props that are overwritten by local props.
recursively merging all properties of both dictionaries into an advantage is that if both dictionaries have for example {style: {}}, it will be merged instead of overwritten
checks if the name & setName are present in props and adds it there if not -- useful for controlled component initialization
useSort hook is a custom hook that enables sorting fucntionality for an array of objects in the react components The hook accepts the following parameters:
- data (array of object) - The initial data to be sorted
- options - An object containing configuration options for sorting :
- column (string) - The key or property of the object to sort by
- sortFn (string or function) - The sorting function to be used. It can be a string representing a predefined sort function or a custom sorting function. By default, the "flex" sorting function is used
- ascending (boolean) - Specifies whether the sorting order is ascending (True) or descending (False). If not provided, the default sorting order is ascending
The hook returns an object with the following properties:
- sorted (array of objects) - The sorted data based on the specified sorting options.
- ascending (boolean) - Indicates the current sorting order (true for ascending, false for descending)
The hook also includes two predefined functions:
- flex - Performs a flexible comparison for sorting. It handles various data types and allows for custom sorting
- num - Performs numeric comparison for sorting. It works specifically for numeric data
import { useSort } from "../../hooks/useSort"
export const Component = ({ data }) => {
const { sorted, ascending } = useSort(data, {
column: "name", //sort by the name column
sortFn: "flex", //sort alphabetically
ascending: true, //sort in ascending order
})
return (
<div>
{sorted.map((row, index) => (
<div key={`rows-${index}`} _prop-target="rows"></div>
))}
</div>
)
}
useDim hook gets updated component prop on window load and resize. Commonly used within the framework to respond to the viewport or container values.
import { useDim } from "../../hooks/useDim"
export const Component = () => {
// by default it will return width and height of the component
const {
ref,
prop: { width, height },
} = useDim()
// but it can be anything with a custom getter - for example a radius of an svg circle
const {
ref: refCircle,
prop: { width, height },
} = useDim({ getter: (c) => c.r.baseVal.value })
return (
<>
<div
// pass to a div directly
ref={ref}
>
<svg>
<circle
cx={"50%"}
cy={"50%"}
r={"calc(50% - 0.5rem)"}
// you need to pass ref to the target component
ref={refCircle}
/>
</svg>
</div>
</>
)
}
useSearch hook is used to filter an array of objects based on a search term and a specified column. It returns the filtered data as a result. It accepts the following parameters:
- data: object[] - The initial array of objects to be filtered
- options: object - An object containing the following options:
- column: string - The column to be searched within each object
- search: string - The search term to filter the data
- searchFn: string or function - The search criteria to be used. Valid options are "flex" that is a greedy search and "exact" that is not greedy.
The hook then returns:
- filtered: object[] - The filtered array of objects based on the search term and column.
import { useSearch } from "../../hooks/useSearch"
export const Component = () => {
const data = [
{ value: "BCG", name: "BCG" },
{ value: "VAR", name: "VAR", selected: true, label: "(1)" },
{ value: "PENTA1", name: "Penta 1" },
{ value: "PENTA3", name: "Penta 3", tooltip: "Second Penta vaccine" },
] // Array of objects to be filtered
const { filtered } = useSearch(data, {
column: "name",
search: "penta 3",
searchFn: "flex",
})
return (
<>
{filtered.map((item) => (
// Render filtered data
<div key={item.value}>{item.name}</div>
))}
</>
)
}
For the full list of props please see generated documentation in [./docs]
Span of a given size and color to imitate the semaphore status. Animated and centered inside the container.
- color: the color of the circle
It is necessary to mention that this component edits default container properties, initializing wrapper and flexbox to make sure it's tightly centered in the middle of the wrapper.
- size - diameter of the span
- circle - span props
<Status
color={"#F6BC8E"}
// pass size directly in the component to make it bigger
size="2rem"
props={{
// alternatively size in the root props if you want to make it bigger
// size: "2rem",
// style it like a square with rounded corners and longer transition
circle: { style: { borderRadius: "0.25rem", transition: "1.5s" } },
}}
/>
React select that exposes click, point and search properties. Allows for custom tooltips of each value/component name. Also allows for search filtering from the dropdown menu options.
- click - feature properties of the currently active (clicked) button
- point - feature properties of the currently hovered (mouseover) button
- search - a string representing the current input in the search field of the dropdown menu
- searchInput - a ref to the search input field, which allows DOM focus manipulation when the dropdown menu is opened
-
options - list of iDropdownOptions objects with at least value, possibly name and selected boolean
- value: string
- selected: boolean
- label?: string
- name?: string
- tooltip?: string
-
label - string to append to a selected label
-
enableSearch - boolean logic to turn on the search functionality in the dropdown
- dropdown - root component (div)
- button - container for label and arrow icon (div)
- label - label and selected name (div and input)
- search - input search string from user (input)
- icon - an svg container (mui SvgIcon)
- menu - container with dropdown options (div)
- option - item in the menu (a)
It is essential for the integrity of the component to not overwrite the display: flex and flex-flow CSS.
It's important for the parent components to have overflow: visible otherwise you are risking the flow of your Dropdown pane.
<Dropdown
label="Coverage: "
options={[
{ value: "BCG", name: "BCG" },
{ value: "VAR", name: "VAR", selected: true, label: "(1)" },
{ value: "PENTA1", name: "Penta 1" },
{ value: "PENTA3", name: "Penta 3", tooltip: "Second Penta vaccine" },
]}
// turn search feature on
enableSearch={true}
/>
Switch/Radio style button with one active button at a time.
- click - feature properties of the currently active (clicked) button
- point - feature properties of the currently hovered (mouseover) button
- options - list of iToggleButtonOption objects with at least value, possibly name and selected boolean
- group - root component (div)
- button - option (button)
- click - CSS of the clicked button (merge with button)
- point - CSS of the pointed button (merge with button)
<ToggleButtonGroup
options={[
// pass config options here
{
name: "Region",
value: "1",
},
// pass active if you want this one to be selected by default
{
name: "District",
value: "2",
selected: true,
},
{
name: "Centre",
value: "3",
},
// you can change the css or the layout by passing components
props: {
// wrapper container
container: {
style: {
padding: 0
}
}
// root component element
group: {
// properties of a group element e.g. group is a div
style: {
// will make the buttons flow vertically
flexDirection: "column"
}
},
button: {
// add button props
type: "button",
formtarget: "form-id",
style: {
// add button styling
}
},
point: {
style: {
// additive style to the pointed at button
}
},
click: {
style: {
// additive style to the clicked at button
}
}
}
]}
/>
React table component to conveniently display out-of-the-box data.
- data - required - list of records (objects or lists) - data for visualization\
- header - list of iHeader type (see [./docs])
- displayHeader - boolean that blanket hides header if needed
- sortArgs - An object containing the initial configuration for the table.
- table - table container (MUI Grid container)
- head - table head row (MUI Grid items row-wise for the first row)
- row - table row (MUI Grid items row-wise)
- cell - each table cell (cell renderer)
The sortArgs are the arguments used to decide how the table should be sorted. These arguments are passed through the setConfig which is a state variable used to manage the column, ascending and sortFn properties which have been explained in the useSort hook
Make sure the tooltip is a stateful variable rather than constant, as when it is set as a constant variable no tooltip can be displayed after partial re-rendering (e.g. filtering).
<Table
container={{
// pass container props. Please be careful as container should be standard
style: {
// one common use case is to pass gridArea to component
gridArea: "table",
},
}}
data={[
{ id: 5, name: "banana", value: 0.53, qt: 10 },
{ id: 2, name: "orange", value: 0.52, qt: 20 },
]}
header={[
// list of dictionaries [iHeader]
{
index: "id",
name: "Product ID",
type: "text",
sort: true //pass sort: true if you want to sort by this column
hidden: false, // set this to true if you want to hide the column
// you can pass custom here, then you need to pass renderer
// renderer: (props) => <div>{props.data {data is a value of a cell}</div> example of simple div around the value
// props: {}, any props you want to send to each cell of a given column
},
// limitation: you need to provide dictionaries equal to the number of column
// if you want to change the order of columns, it should contain at least index
{},
{},
{},
]}
props={{
table: {
// passed in table container [Mui Grid]
// xs: 2, //other mui-grid properties
style: { backgroundColor: "white" },
},
row: {
// passed to each cell that in a row [Mui Item]
},
cell: {
// passed to each cell
},
}}
displayHeader={true}
/>
React map component built around react-map-gl & mapbox-gl to rapidly develop map components.
By default, Map generates a significant amount of subscribers and observers to ease the development. They can be overridden by providing an empty init prop.
By default, the label is called "name" (e.g. org unit name) By default, the value is called "value" You can add more keys in properties to enrich map tooltips as currently tooltips are being rendered as
label: name
variable: value units
current tooltips are generated using the method that takes in corresponding info state and can be easily overridden (see iMap)
I am still deciding whether it's better to pass label/name and variable in props and override it, decision pending
- click - feature properties of currently active (clicked) polygon\
- point - feature properties of currently hovered (mouseover) polygon\
- controller - an object with states and functions of currently rendered maps\
- controller.fitBounds - function to fit current map bounds (takes bound array [[leftTop, rightTop],[leftBottom, rightBottom]])\
- controller.allLabels - all "name" properties existing in the geojson
Basic map usage only consists of data, colors and steps
still deciding whether color and steps should be a config prop passed together > with values. Exposing createPalette method and leave it for a user to generate palette might just be a simpler solution
- data - geojson with feature properties\
- colors - a list of colors in hex or rgb\
- steps - a list of color steps
!important Since colors fit in the gaps, there should be 1 more color than the steps
- map - root map component (react-map-gl Map)
- layers - each map layer
- tooltip - both point and click tooltips (Tooltip component, wrapper for rendering function)
Generally speaking, we are following MapboxGL layer model. To create a new layer, add a layer of the following format under layers in the props passthrough:
[layer-id] : {
type: [layer-type],
paint: {
...[paint-props]
},
layout: {
...[layout-props]
},
filter: [layer-filter]
}
The filter should be an expression-type prop that is following MapboxGL model ["==", "name", f(states)] with one exception: the third argument is a function of states that returns value to compare the expression to. States include click, point etc.
The data layer is a bit of a special case. You can still pass every layer-specific property like layout, but you want to leave paint-[type]-color property to be generated automatically based on steps, colors and value columns.
[layer-id] : {
type: [layer-type],
paint: {
...[paint-props]
},
layout: {
...[layout-props]
},
filter: [layer-filter],
// data-specific-props
steps: [steps],
colors: [colors],
naColor: [naColor | "#BFBFBF"],
valueColumn: [valueColumn | "value"]
}
Usually, you don't want colors or steps fixed in your layout props if you are not working with monotoneous same-type data across the entire application and instead pass dynamic steps or colors or both in your component props instead.
The downside of adding layers is that if any layers beyond defaults need to be added, a user has to specify all layers that they want to be displayed - that generate redundancy. See an example below.
<Map
{...props}
// this layer will be added to the default layers list
props={{
layers: {
label: {
type: "symbol",
layout: {
"text-field": ["get", "name"],
"text-offset": [0, 0.6],
"text-anchor": "center",
"text-size": 10,
},
},
},
}}
// you have to specify all layers that need to be displayed
layers={["outline", "blur", "highlight", "label"]}
/>
Available layers are: data - choropleth by default (mandatory) outline - border around each polygon (enabled) blur - blur all other polygons when one of them is clicked (enabled) highlight - highlight border around the clicked polygon (enabled) label - display name of the polygon (disabled)
The tooltip has at least 5 values it sources from the geojson data: label, variable, name, value and units.
- name - name of the highlighted area
- label - name description/ highlighted area level
- variable - value description
- units - value suffix
These variables can be added to the geojson as given in the example below:
const geoData = geoJson.features.map((e) => ({
...e,
properties: {
...e.properties,
variable: "BCG",
units: "%",
label: "Region"
},
}))
return (
<div>
<Map
data = {{...geoJson, features: geoData }}
colors = {colors}
steps={steps}
/>
<div/>
)
const colors = [
"#000000",
"#006837",
"#a50026",
"#d73027",
"#f46d43",
"#fdae61",
"#fee08b",
"#ffffbf",
"#d9ef8b",
"#66bd63",
"#1a9850",
"#006837",
]
const steps = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
<div style={{ height: "100vh", width: "100vw" }}>
// consume config here
<ComponentContextProvider config={appConfig}>
// data here is geojson that contains at least name and value feature
properties
<Map data={data} colors={colors} steps={steps}>
// you can pass extra children to the map // since legend is only
connected to a map with colors and not logic, it makes sense to pass
it separately
<MapLegend colors={colors} steps={steps} />
// controls are reexported from react-map-gl
<MapFullscreenControl />
</Map>
</ComponentContextProvider>
</div>
For examples of creating custom events you can read components/Map/logic files, there is an example coming from the default logic of the map
const useMapStates = (subscribers, observers, props) => {
// adding states
checkState("point", props)
checkState("click", props)
// adding onMove sub
subscribers.unshift({
on: "move",
func: (event) => {
const featureData = getInfoFromMapEvent(event)
props["setPoint"](featureData)
},
})
subscribers.unshift({
on: "click",
func: (event) => {
let featureData = getInfoFromMapEvent(event)
if (featureData) {
featureData =
props.click?.name === featureData.name ? undefined : featureData
}
props["setClick"](featureData)
},
})
observers.unshift({
args: ["controller", "click", "bounds", "data"],
func: (args) => {
if (args.click) {
const newBound = getBound(args.click?.feature
C403
span>?.geometry.coordinates)
const noClickRegions = args.controller.allLabels.filter(
(l) => l !== args.click.name
)
args.click["noClick"] = noClickRegions
args.controller?.fitBounds(newBound)
} else {
args.controller?.fitBounds(args.bounds)
}
},
prop: "click",
})
}
React component built around SVG to help visualize data conveniently . Has the ability to visualize infinite progress and render customized content at the center.
- value - required - the proportion of the whole to be represented.
- color?: string
- minValue?: number
- maxValue?: number
- suffix?: string | any
- props?: Any
- circle - the SVG element representing the whole
- total - the SVG element representing the total
- progress - the SVG element representing the progress
- value - the element containing the value
- counter - a counter value to aid in animations (from react-countup)
// progress into my savings
<ProgressCircle value={100} minValue={10} maxValue={150} suffix={"$"} />
React component that displays a progress bar based on a given value\
- value - required - represents the current value of the progress bar.
- color - optional - sets the color of the progress bar. The default value is "#7AA995".
- maxValue – optional - sets the maximum value of the progress bar. The default value is 100.
- minValue - optional - sets the minimum value of the progress bar. The default value is 0.
- suffix – optional - sets the suffix to be added to the value displayed in the progress bar. The default value is "%".
- props – optional – allows for additional properties to be passed to the element
- bar – root component (div)
- total – container for current progress (div)
- progress – style formatting on current progress
- labels – container for the labels(div)
- label – style formatting on each label
<ProgressBar
value="85"
props={{
// root component container
bar: {
style: {
padding: 0,
margin: 0,
},
},
// total div wraps progress bar
total: {},
progress: {
// progress bar is set to a % of a total div
},
labels: {
// holds the progress bar labels (min and max value)
},
label: {
// additive styling formats for the labels used (min, max, progress value)
},
value: {
// specific styling for progress value
},
}}
/>
React component built on top of the Recharts library to help visualize data on a line chart.
- data - required : List of objects of rows
- traces - required: an object to specify the data elements { dataKey: string stroke: string activeDot?: any }[]
- children - Additional Chart components like
<XAxis />
,<YAxis />
,<CartesianGrid />
and<Legend />
with their respective customized properties. - props - optional: allows for passing additional properties to the component
- chart -
<ResponsiveContainer />
element properties - trace -
<Line />
element properties - tooltip -
<Tooltip />
element properties
import { ChartLegend, ChartGrid } from '@dalbergdatainsights/react-components'
// visualizing the monthly vaccination levels for two drugs x and y.
const data = [
{ "pe": "Jan", "x": 4, "y": 8 },
{ "pe": "Feb", "x": 5, "y": 16 },
{ "pe": "Mar", "x": 8, "y": 32 },
{ "pe": "Apr", "x": 20, "y": 48 },
{ "pe": "May", "x": 35, "y": 56 },
{ "pe": "Jun", "x": 37, "y": 69 },
{ "pe": "Jul", "x": 45, "y": 78 },
{ "pe": "Aug", "x": 39, "y": 89 },
{ "pe": "Sep", "x": 46, "y": 88 },
{ "pe": "Oct", "x": 53, "y": 94 },
{ "pe": "Nov", "x": 54, "y": 96 },
{ "pe": "Dec", "x": 60, "y": 100 },
]
// plot
<LineChart data={data}
X={'pe'}
traces={[
{dataKey: "x", stroke: "#D1D1D6"},
{dataKey: "y", stroke: "#8E8E93"},
]}
>
<ChartLegend />
<ChartGrid />
</LineChart>
React component built on top of the Recharts library to help visualize data on a chart.
- data - required: list of objects of rows
- traces - optional: list of objects to specify which data columns to visualize with dataKey at the very least { dataKey: string }[] (see iTrace)
- axis - optional: a list of exact length 2 [xaxis, yaxis] - properties to unwrap in X and Y axis in the component. Can pass [undefined, {xaxis}] to remove one of them (see iAxis)
- children - Additional Chart components like
<XAxis />
,<YAxis />
,<CartesianGrid />
and<Legend />
with their respective customized properties. - props - optional: allows for passing additional properties to the component
- chart:
<ResponsiveContainer />
element properties - trace:
<Line />
element properties - tooltip:
<Tooltip />
element properties - x/yaxis:
<XAxis />
and<YAxis />
import { ChartLegend, ChartGrid, Chart } from '@dalbergdatainsights/react-components'
// visualizing the monthly vaccination levels for two drugs x and y.
const data = [
{ "pe": "Jan", "x": 4, "y": 8 },
{ "pe": "Feb", "x": 5, "y": 16 },
{ "pe": "Mar", "x": 8, "y": 32 },
{ "pe": "Apr", "x": 20, "y": 48 },
{ "pe": "May", "x": 35, "y": 56 },
{ "pe": "Jun", "x": 37, "y": 69 },
{ "pe": "Jul", "x": 45, "y": 78 },
{ "pe": "Aug", "x": 39, "y": 89 },
{ "pe": "Sep", "x": 46, "y": 88 },
{ "pe": "Oct", "x": 53, "y": 94 },
{ "pe": "Nov", "x": 54, "y": 96 },
{ "pe": "Dec", "x": 60, "y": 100 },
]
<Chart
// Linechart, see recharts to see available charts
type="Line"
data={data}
traces={[
{dataKey: "x", stroke: "#D1D1D6"},
{dataKey: "y", stroke: "#8E8E93"},
]}
// XAxis pe, YAxis display with no label
axis={[{dataKey: "pe"}, {}]}
>
<ChartLegend />
<ChartGrid />
</Chart>
The component that allows mapping areas of the screen arbitrarily. More on CSS grids and grid templates can be read here: https://css-tricks.com/wp-content/uploads/2022/02/css-grid-poster.png
- areas: list of lists of grid areas (see example)
- columns: list of column sizes
- rows: list of row sizes
sizes support all grid sizes, including common px, rem, vh as well as relative as fr children and style are passed through as usual
<ComponentContextProvider config={{}}>
<div style={{ height: "100vh", width: "100vw", backgroundColor: "white" }}>
<NamedGrid
columns={["5fr", "3fr", "4fr"]} // divide into 5,3 and 4 fractions of the screen
rows={["2fr", "5fr", "5fr", "1fr"]} // divide into 2, 5, 5, 1 fraction
// define grid areas to attach components do
areas={[
["area1", "area3"],
["area2", "area3"],
["footer", "footer"],
]}
>
<Table
data={[{ index: 2, name: "orange", value: 0.52, qt: 20 }]}
// unmarked components will go in the first available area, but only in 1 slot
/>
<Table
// you can pass gridArea to any component's style
container={{
style: { gridArea: "area3" },
}}
data={[
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
{ index: 2, name: "apple", value: 0.52, qt: 20 },
]}
/>
<Table data={[{ index: 2, name: "lemon", value: 0.52, qt: 20 }]} />
<Empty
gridArea="footer"
color="yellow"
// or define a component that takes gridArea in props
/>
</NamedGrid>
</div>
</ComponentContextProvider>
Make sure that you follow the philosophy and the assumptions of the framework
- Add component folder
- Separate component render, logic, config
- Define component manifest
- Instantiate export with the wrapper from related file (components/core/layouts)
- Add a README section for the component
- Generate a new table of content (https://ecotrust-canada.github.io/markdown-toc/)
- Generate documentation
- Iterate a version
- Install typedoc
- Install typedoc-plugin-markdown
- Install typedoc-plugin-missing-exports
- Run
npx typedoc --plugin typedoc-plugin-markdown --plugin typedoc-plugin-missing-exports
- If (4) does not work, try
npx typedoc --plugin typedoc-plugin-markdown --skipErrorChecking
Don't forget to increment your version before publishing!