Skip to content

Commit

Permalink
JNG-5993 card representation (#486)
Browse files Browse the repository at this point in the history
  • Loading branch information
noherczeg authored Nov 11, 2024
1 parent 7389c8b commit a79fafe
Show file tree
Hide file tree
Showing 16 changed files with 956 additions and 0 deletions.
105 changes: 105 additions & 0 deletions docs/pages/01_ui_react.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,111 @@ const SidekickGalaxiesComponent: FC<SidekickComponentProps<ViewGalaxyStored>> =
};
----

=== Implementing Card Containers for Tables with Card representations

We can model tables to represent cards. In case we did so, by default we will get a simplistic
representation of cards. To specify how each card should look like (and other aspects as wel), we
can register a configuration.

The interface key for these hooks is the unique name of the table plus "_CARDS_CONTAINER_CONFIG_HOOK_INTERFACE_KEY".

*src/custom/application-customizer.tsx:*
[source,typescriptjsx]
----
import type { BundleContext } from '@pandino/pandino-api';
import type { ApplicationCustomizer } from './interfaces';
import {
VIEW_GALAXY_TABLE_TABLE_COMPONENT_CARDS_CONTAINER_CONFIG_HOOK_INTERFACE_KEY,
ViewGalaxyTableTableComponentCardsContainerConfigHook,
} from '~/containers/View/Galaxy/Table/components/ViewGalaxyTableTableComponent/customization';
import {CardProps, ToolbarElementProps } from '~/components-api/components/CardsContainer';
import {ViewGalaxy, ViewGalaxyStored } from '~/services/data-api/model/ViewGalaxy';
import {CardsFilter, CardsFilterDefinition } from '~/components/widgets/CardsFilter';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
import CardActions from '@mui/material/CardActions';
import Button from '@mui/material/Button';
import Grid from '@mui/material/Grid';
import { Filter, FilterType } from '~/components-api';
import { _BooleanOperation, _StringOperation } from '~/services/data-api/common';
import { mapCardsFiltersToFilters } from '~/utilities';
export class DefaultApplicationCustomizer implements ApplicationCustomizer {
async customize(context: BundleContext): Promise<void> {
context.registerService<ViewGalaxyTableTableComponentCardsContainerConfigHook>(
VIEW_GALAXY_TABLE_TABLE_COMPONENT_CARDS_CONTAINER_CONFIG_HOOK_INTERFACE_KEY,
cardsConfigHook
);
}
}
const cardsConfigHook: ViewGalaxyTableTableComponentCardsContainerConfigHook = () => {
// We are returning a full configuration, but all of the attributes below are optional
return {
layout: 'horizontal',
ToolbarElement: CustomToolbar, // a valid React component implementing FC<ToolbarElementProps<T>>
CardElement: CustomCard, // a valid React component implementing FC<CardProps<T>>
};
};
function CustomToolbar({ handleFiltersChange }: ToolbarElementProps<ViewGalaxyStored>) {
// We can call other hooks here
const filterDefs: CardsFilterDefinition<ViewGalaxy>[] = [
{
type: FilterType.boolean, field: 'nakedEye', label: 'Naked Eye',
values: [
{ value: true, label: 'Yes' },
{ value: false, label: 'No' }
],
},
{
type: FilterType.string, field: 'constellation', label: 'Constellation',
values: [
{ value: 'Andromeda', label: 'Andromeda' },
{ value: 'Corvus', label: 'Corvus' },
{ value: 'Ursa Major', label: 'Ursa Major',
},
],
},
];
// We are using a built in component CardsFilter here, but we can implement anything
return (
<CardsFilter filterDefinitions={filterDefs} onFiltersChanged={(values: Record<string, any>) => {
const newFilters: Filter[] = mapCardsFiltersToFilters(filterDefs, values);
handleFiltersChange(newFilters);
}} />
);
}
function CustomCard({ row, columns, onRowClick }: CardProps<ViewGalaxyStored>) {
// We can call other hooks here
// Every custom Card component MUST be wrapped in a `<Grid item` with the appropriate `sm`, `md`, etc... values so that
// elements are handled properly on all devices.
return (
<Grid item sm={12} md={6} lg={4} xl={4}>
<Card variant="outlined" sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
<CardContent>
{columns.map((k, idx) => (
<Typography key={`${row.__identifier}-${k.field}`} variant={idx === 0 ? 'h5' : 'body2'}>
{row[k.field as keyof ViewGalaxyStored]?.toString()}
</Typography>
))}
</CardContent>
<CardActions>
<Button size="small" onClick={() => onRowClick?.(row)}>
Learn More
</Button>
</CardActions>
</Card>
</Grid>
);
}
----

=== Implementing container actions

Every container has a set of Actions. These are typically actions triggered by buttons, or visual lifecycle calculated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ public static List<Table> getTagsForPageContainers(Application application) {
.collect(Collectors.toList());
}

public static List<Table> getCardsForPageContainers(Application application) {
return application.getPageContainers().stream().flatMap(c -> ((List<Table>) c.getTables()).stream())
.filter(UiTableHelper::isTableCard)
.collect(Collectors.toList());
}

public static String pageContainerActionDefinitionsName(PageContainer pageContainer) {
return containerComponentName(pageContainer) + "ActionDefinitions";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,4 +310,8 @@ public static boolean checkboxSelectionEnabled(Table table) {
public static boolean isTableTag(Table table) {
return TableRepresentation.TAG.equals(table.getRepresentationComponent());
}

public static boolean isTableCard(Table table) {
return TableRepresentation.CARD.equals(table.getRepresentationComponent());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ public static String tagComponentName(Table table) {
return tableComponentName(table);
}

public static String cardsComponentName(Table table) {
return tableComponentName(table);
}

public static Column getFirstAutocompleteColumnForLink(Link link) {
Optional<Column> column = link.getParts().stream()
.filter(c -> c.getAttributeType().getDataType() instanceof StringType && !c.getAttributeType().getIsMemberTypeTransient())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type {
GridColDef,
GridValidRowModel,
GridSortModel,
} from '@mui/x-data-grid{{ getMUIDataGridPlanSuffix }}';
import type { Filter, FilterOption } from '~/components-api';

export interface CardProps<T extends GridValidRowModel> {
row: T;
columns: GridColDef<T>[];
onRowClick?: (row: T) => void;
}

export interface ToolbarElementProps<T extends GridValidRowModel> {
columns: GridColDef<T>[],
filterOptions: FilterOption[];
filters: Filter[];
handleFiltersChange: (newFilters: Filter[]) => void,
sortModel: GridSortModel,
handleSortModelChange: (newModel: GridSortModel) => void,
refresh: () => Promise<void>,
isLoading: boolean,
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
export * from './Action';
export * from './ActionGroup';
export * from './Button';
export * from './CardsContainer';
export * from './LabeledElement';
export * from './MenuTree';
export * from './NamedElement';
Expand Down
Loading

0 comments on commit a79fafe

Please sign in to comment.