-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simple table with data from external API #2416
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import React from 'react'; | ||
|
||
import { Link, Table } from '@digdir/designsystemet-react'; | ||
import dot from 'dot-object'; | ||
|
||
import { Caption } from 'src/components/form/Caption'; | ||
import { useExternalApi } from 'src/features/externalApi/useExternalApi'; | ||
import { ErrorList } from 'src/layout/GenericComponent'; | ||
import { useNodeItem } from 'src/utils/layout/useNodeItem'; | ||
import type { PropsFromGenericComponent } from 'src/layout'; | ||
import type { ColumnConfig } from 'src/layout/SimpleTable/config.generated'; | ||
|
||
export const SimpleTableComponent = ({ node }: PropsFromGenericComponent<'SimpleTable'>) => { | ||
const nodeItem = useNodeItem(node); | ||
|
||
const { data: externalApi } = useExternalApi(nodeItem.data.id); | ||
const data: unknown = dot.pick(nodeItem.data.path, externalApi); | ||
|
||
if (!isArrayOfObjects(data)) { | ||
return ( | ||
<ErrorList | ||
nodeId={node.id} | ||
errors={['Tabelldata må være en liste av objekter']} | ||
/> | ||
); | ||
} | ||
|
||
return ( | ||
<Table width='100%'> | ||
<Caption title={nodeItem.title} /> | ||
<Table.Head> | ||
<Table.Row> | ||
{nodeItem.columns.map((column) => ( | ||
<Table.HeaderCell key={column.id}>{column.title}</Table.HeaderCell> | ||
))} | ||
</Table.Row> | ||
</Table.Head> | ||
<Table.Body> | ||
{data.length === 0 && ( | ||
<Table.Row> | ||
<Table.Cell colSpan={nodeItem.columns.length}>Ingen data</Table.Cell> | ||
</Table.Row> | ||
)} | ||
{data.map((row) => ( | ||
<Table.Row key={String(row)}> | ||
{nodeItem.columns.map((column, idx) => ( | ||
<Table.Cell key={`${column.id}[${idx}]`}>{renderCell(column.component, row)}</Table.Cell> | ||
))} | ||
</Table.Row> | ||
))} | ||
</Table.Body> | ||
</Table> | ||
); | ||
}; | ||
|
||
function renderCell(component: ColumnConfig['component'], row: Record<string, unknown>) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably just be a react component instead. 🙌 |
||
if (component.type === 'Link') { | ||
return ( | ||
<Link | ||
href={dot.pick(component.hrefPath, row)} | ||
target='_blank' | ||
> | ||
{dot.pick(component.textPath, row)} | ||
</Link> | ||
); | ||
} | ||
|
||
// FIXME: what if the path does not exist and the resulting value is undefined? | ||
// Should we display an error, or is this a valid use case in production? | ||
return dot.pick(component.valuePath, row); | ||
} | ||
|
||
function isArrayOfObjects(data: unknown): data is Record<string, unknown>[] { | ||
return Array.isArray(data) && data.every((row: unknown) => !!row && typeof row === 'object' && !Array.isArray(row)); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { CG } from 'src/codegen/CG'; | ||
import { CompCategory } from 'src/layout/common'; | ||
|
||
export const Config = new CG.component({ | ||
category: CompCategory.Presentation, | ||
capabilities: { | ||
renderInTable: false, | ||
renderInButtonGroup: false, | ||
renderInAccordion: false, | ||
renderInAccordionGroup: false, | ||
renderInCards: false, | ||
renderInCardsMedia: false, | ||
renderInTabs: true, | ||
}, | ||
functionality: { | ||
customExpressions: false, | ||
}, | ||
}) | ||
.extends(CG.common('LabeledComponentProps')) | ||
.addProperty(new CG.prop('title', new CG.str())) | ||
.addProperty( | ||
new CG.prop( | ||
'columns', | ||
new CG.arr( | ||
new CG.obj( | ||
new CG.prop('id', new CG.str()), | ||
new CG.prop('title', new CG.str()), | ||
new CG.prop( | ||
'component', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would probably call this |
||
new CG.union( | ||
new CG.obj(new CG.prop('type', new CG.const('Text')), new CG.prop('valuePath', new CG.str())), | ||
new CG.obj( | ||
new CG.prop('type', new CG.const('Link')), | ||
new CG.prop('hrefPath', new CG.str()), | ||
new CG.prop('textPath', new CG.str()), | ||
), | ||
).setUnionType('discriminated'), | ||
), | ||
).exportAs('ColumnConfig'), | ||
), | ||
), | ||
) | ||
.addProperty( | ||
new CG.prop( | ||
'data', | ||
new CG.obj( | ||
new CG.prop('type', new CG.const('externalApi')), | ||
new CG.prop('id', new CG.str()), | ||
new CG.prop('path', new CG.str()), | ||
).exportAs('DataConfig'), | ||
), | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React, { forwardRef } from 'react'; | ||
import type { JSX } from 'react'; | ||
|
||
import { SimpleTableDef } from 'src/layout/SimpleTable/config.def.generated'; | ||
import { SimpleTableComponent } from 'src/layout/SimpleTable/SimpleTableComponent'; | ||
import type { PropsFromGenericComponent } from 'src/layout'; | ||
|
||
export class SimpleTable extends SimpleTableDef { | ||
render = forwardRef<HTMLElement, PropsFromGenericComponent<'SimpleTable'>>( | ||
function LayoutComponentSimpleTableRender(props, _): JSX.Element | null { | ||
return <SimpleTableComponent {...props} />; | ||
}, | ||
); | ||
} |
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.
Instead of rendering out an ErrorList, just throw an error instead. It will be caught in the error handler, logged to devtools, and then displayed in an error list. I would prefer not to export the ErrorList, as it just makes it possible to circumvent the normal way these errors are caught and logged.