Skip to content
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

Grid component #1118

Merged
merged 83 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from 82 commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
8d9d926
Adding types and basic component infrastructure fro Grid
Feb 27, 2023
7bcfda1
Fixing type issues in other code now that multiple components have 'r…
Feb 27, 2023
70f74ed
Basic (possibly even working) support for Grid layout in hierarchy
Feb 27, 2023
778a56d
Merge branch 'main' into feat/grid
Feb 28, 2023
c48306c
Removing expression support temporarily. After the major type changes…
Feb 28, 2023
17f0c3d
Fixing other cypress tests so they work after `frontend-test` changes…
Feb 28, 2023
5a3820d
Fixing other cypress tests so they work after `frontend-test` changes…
Feb 28, 2023
11a946b
Merge branch 'main' into feat/grid
Mar 13, 2023
90d0c98
Fixes after merge fom main
Mar 13, 2023
bc5dad1
Early working prototype
Mar 15, 2023
70e58c8
Streamlining code to avoid rendering an empty cell in two different w…
Mar 15, 2023
b3eb626
Merge branch 'main' into feat/grid
Mar 15, 2023
499ddec
Oooh, apparently we _can_ use the design system here (Webstorm just c…
Mar 15, 2023
25ea79a
Converting getComponent() to a statically defined `def`
Mar 16, 2023
f298cdd
Adding some utility functions for checking the type of component insi…
Mar 16, 2023
5019103
Moving LayoutNode, LayoutPage, LayoutPages and LayoutObject classes i…
Mar 16, 2023
dc54c67
Simplifying the way ExprContext generates its resolved nodes (re-usin…
Mar 16, 2023
4280454
Making ExprContext more efficient again, avoiding re-running expressi…
Mar 16, 2023
ac04ff8
Woah, I kinda started off with a big undertaking, and after a lot of …
Mar 21, 2023
aa7db64
Merge branch 'main' into chore/niceties-in-layoutnode
Mar 21, 2023
49bd10f
Merge branch 'main' into feat/grid
Mar 24, 2023
4e096f6
Re-implementing overrides in checkboxes/radios after merge from main
Mar 24, 2023
2410baa
Fixing my previous commit. This is normally undefined, which is not t…
Mar 27, 2023
83940ec
Merge branch 'main' into chore/niceties-in-layoutnode
Apr 3, 2023
b62d768
Merge branch 'main' into feat/grid
Apr 3, 2023
53e86b1
Cleanup after merge from main
Apr 3, 2023
9a431e4
Limiting claims to only one parent
Apr 3, 2023
737c386
Moving the IGroupPanel type, as it more clearly belongs to the Group …
Apr 3, 2023
c6782e9
Moving the different stage functions into a common class that can be …
Apr 3, 2023
bb17a66
Re-implementing panel group references
Apr 4, 2023
92e3a9a
Inching closer to making the hierarchy generator work across multiple…
Apr 4, 2023
5b25a7c
Adding unit-test for what's left (and failing) in processing panels w…
Apr 5, 2023
8a64779
Adding a stage 3 callback to make some of the unit-tests for group re…
Apr 11, 2023
e28e4a3
Making sure stages run to completion before starting processing the n…
Apr 11, 2023
4999314
Merge branch 'main' into chore/niceties-in-layoutnode
Apr 11, 2023
51eeb94
Introducing function as parameter to avoid circular import problem th…
Apr 11, 2023
d7ccbf0
Fixing unit-tests that were failing because of bad configuration
Apr 11, 2023
39f1578
GenericComponent will never get an unknown component type after this,…
Apr 11, 2023
4279ad0
Real apps sometimes create list of children where not all of them exi…
Apr 11, 2023
c8e8c29
Fixing the component type in shared tests - I mistakenly used the wro…
Apr 11, 2023
bf028d9
Preventing infinite recursion in tests now that node references are i…
Apr 11, 2023
c83bcba
Fixing the definition of what a repeating group is. This was off by one.
Apr 11, 2023
8942cd5
Minor cleanup
Apr 11, 2023
5738047
Adding code to test all existing apps, and fixing some problems I fou…
Apr 12, 2023
cb6b954
Fixing mapping rewrites, which failed because I failed to account for…
Apr 12, 2023
7ddce53
Merge branch 'main' into feat/grid
Apr 12, 2023
771aa91
Adding layout schema for Grid component
Apr 12, 2023
f2da040
Only allowing header/readOnly props for rows, not individual cells
Apr 12, 2023
def9836
Merge branch 'chore/niceties-in-layoutnode' into feat/grid
Apr 13, 2023
f7b7380
Re-implementing the hierarchy generator for Grid
Apr 13, 2023
6e19664
Disallowing some components from being rendered in the table/grid
Apr 13, 2023
6c18e6b
Merge branch 'main' into feat/grid
Apr 13, 2023
166d1a6
Hising all components on a row also hides the row
Apr 13, 2023
22ff35d
Setting !important to override table padding from the design system
Apr 13, 2023
ce81ef2
Adding label overrides (for a future version of the design system, I …
Apr 13, 2023
a2b8541
Fixing labels for other components when rendering in table
Apr 14, 2023
8784de9
Adding support for text resources
Apr 14, 2023
c9a63e9
Adding some test data to use when filling out grid
Apr 14, 2023
7121117
Fixing issues when skipping ahead - we have to wait for GridData to l…
Apr 14, 2023
bc0d492
add column options for grid
Magnusrm Apr 17, 2023
fce0bce
more specific css selector in order to override default padding
Magnusrm Apr 17, 2023
80cecbc
Fixing some cypress tests that are failing. Creating a function to fi…
Apr 17, 2023
958d495
Adding a simple test for the Grid component
Apr 17, 2023
9cf4bc5
widths should sum up to 100%
Magnusrm Apr 17, 2023
096d2c2
Extracting hidden row functionality to a function
Apr 17, 2023
1942fa1
Implementing a super-simple mobile grid (just deconstructing into reg…
Apr 17, 2023
dca82df
Implementing a simple way to render grid components in a Summary
Apr 18, 2023
47a83d4
Fixing the navigation so that clicking to edit summary navigates to t…
Apr 18, 2023
45660df
Fixing summaries for checkboxes when no value has been selected. This…
Apr 18, 2023
d7075cf
Adding tests for summary and mobile mode
Apr 18, 2023
ebf70df
Merge remote-tracking branch 'origin/feat/grid' into feat/grid
Apr 18, 2023
072b7ec
Merge branch 'main' into feat/grid
Apr 18, 2023
ff904a0
Cleaning up tests I changed earlier in the branch history
Apr 18, 2023
a03d667
Fixing the navigation routine again, after I broke it by navigating t…
Apr 18, 2023
6077ec8
Making .navPage() work on mobile as well
Apr 18, 2023
aded0b8
Extracting out methods and simplifying implementation of mobile grid.…
Apr 18, 2023
a48c288
The mobile.ts cypress test failed after this change, because the like…
Apr 18, 2023
3e10949
Merge branch 'main' into feat/grid
Apr 19, 2023
a9f3ebb
Fixing problem with cells, where a later cell could overwrite the hea…
Apr 20, 2023
6e27455
Merge branch 'main' into feat/grid
Apr 25, 2023
4201f30
Fixing aria-label property after merge from main
Apr 25, 2023
0b72fa8
Fixing failing unit test
Apr 25, 2023
e0df1c4
Removing console.log
Apr 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 73 additions & 2 deletions schemas/json/layout/layout.schema.v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"type": "string",
"title": "Type",
"description": "The component type.",
"enum": ["AddressComponent", "AttachmentList", "Button", "Checkboxes", "Custom", "Datepicker", "Dropdown", "FileUpload", "FileUploadWithTag", "Group", "Header", "Image", "Input", "InstanceInformation", "InstantiationButton", "Likert","List", "MultipleSelect", "NavigationButtons", "NavigationBar", "Panel", "Paragraph", "PrintButton", "RadioButtons", "Summary", "TextArea"]
"enum": ["AddressComponent", "AttachmentList", "Button", "Checkboxes", "Custom", "Datepicker", "Dropdown", "FileUpload", "FileUploadWithTag", "Grid", "Group", "Header", "Image", "Input", "InstanceInformation", "InstantiationButton", "Likert","List", "MultipleSelect", "NavigationButtons", "NavigationBar", "Panel", "Paragraph", "PrintButton", "RadioButtons", "Summary", "TextArea"]
},
"required": {
"title": "Required",
Expand Down Expand Up @@ -130,6 +130,7 @@
{ "if": {"properties": {"type": { "const": "Dropdown"}}}, "then": { "$ref": "#/definitions/selectionComponents"}},
{ "if": {"properties": {"type": { "const": "FileUpload"}}}, "then": { "$ref": "#/definitions/fileUploadComponent"}},
{ "if": {"properties": {"type": { "const": "FileUploadWithTag"}}}, "then": { "$ref": "#/definitions/fileUploadWithTagComponent"}},
{ "if": {"properties": {"type": { "const": "Grid"}}}, "then": { "$ref": "#/definitions/gridComponent"}},
{ "if": {"properties": {"type": { "const": "Group"}}}, "then": { "$ref": "#/definitions/groupComponent"}},
{ "if": {"properties": {"type": { "const": "Image"}}}, "then": { "$ref": "#/definitions/imageComponent"}},
{ "if": {"properties": {"type": { "const": "Input"}}}, "then": { "$ref": "#/definitions/inputComponent"}},
Expand Down Expand Up @@ -452,6 +453,71 @@
}
}
},
"gridComponent": {
"properties": {
"rows": {
"title": "Rows",
"description": "An array of rows to be rendered in the grid.",
"type": "array",
"items": {
"$ref": "#/definitions/gridRow"
}
}
},
"required": ["rows"]
},
"gridRow": {
"properties": {
"header": {
"title": "Header?",
"description": "A boolean indicating if the row should be a header row",
"type": "boolean",
"default": false
},
"readOnly": {
"title": "Read only?",
"description": "A boolean indicating if the row should be a read only row (yellow background)",
"type": "boolean",
"default": false
},
"cells": {
"title": "Grid row cells",
"description": "An array of cells to be rendered in the row",
"type": "array",
"items": {
"anyOf": [
{ "$ref": "#/definitions/gridCellText" },
{ "$ref": "#/definitions/gridCellComponent" },
{ "$ref": "#/definitions/tableColumnOptions" },
{ "type": "null", "title": "Empty cell" }
]
}
}
},
"required": ["cells"]
},
"gridCellText": {
"properties": {
"text": {
"title": "Text",
"description": "The text or text resource ID to be rendered in the cell",
"type": "string"
}
},
"$ref": "#/definitions/tableColumnOptions",
"required": ["text"]
},
"gridCellComponent": {
"properties": {
"component": {
"title": "Component ID",
"description": "The ID of the component to be rendered in the cell",
"type": "string"
}
},
"$ref": "#/definitions/tableColumnOptions",
"required": ["component"]
},
"groupComponent": {
"properties": {
"children": {
Expand Down Expand Up @@ -626,7 +692,12 @@
"description": "Width of cell in % or 'auto'. Defaults to 'auto'",
"type": "string",
"pattern": "^([0-9]{1,2}%|100%|auto)$"
},
}
},
"$ref": "#/definitions/tableColumnTextOptions"
},
"tableColumnTextOptions": {
"properties": {
"alignText": {
"title": "Align Text",
"description": "Choose text alignment between 'left', 'center', or 'right' for text in table cells. Defaults to 'left' for text and 'right' for numbers.",
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useCommaSeparatedOptionsToText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { ISelectionComponent } from 'src/layout/layout';
export function useCommaSeparatedOptionsToText(component: ISelectionComponent, value: string) {
const textResources = useAppSelector((state) => state.textResources.resources);
const optionList = useOptionList(component);
const split = value.split(',');
const split = value.split(',').filter((value) => !!value.trim());
const out: { [key: string]: string } = {};
split?.forEach((part) => {
const textKey = optionList.find((option) => option.value === part)?.label || '';
Expand Down
4 changes: 4 additions & 0 deletions src/layout/Address/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ export class Address extends FormComponent<'AddressComponent'> {
const data = this.useDisplayData(targetNode);
return <SummaryItemSimple formDataAsString={data} />;
}

canRenderInTable(): boolean {
return false;
}
}
4 changes: 4 additions & 0 deletions src/layout/AttachmentList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ export class AttachmentList extends PresentationComponent<'AttachmentList'> {
renderWithLabel(): boolean {
return false;
}

canRenderInTable(): boolean {
return false;
}
}
16 changes: 13 additions & 3 deletions src/layout/Checkboxes/CheckboxesContainerComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export const CheckboxContainerComponent = ({
language,
handleDataChange,
getTextResource,
getTextResourceAsString,
overrideDisplay,
}: ICheckboxContainerProps) => {
const {
id,
Expand Down Expand Up @@ -103,6 +105,8 @@ export const CheckboxContainerComponent = ({
</span>
);

const hideLabel = overrideDisplay?.renderedInTable === true && calculatedOptions.length === 1;

return fetchingOptions ? (
<AltinnSpinner />
) : (
Expand All @@ -115,9 +119,14 @@ export const CheckboxContainerComponent = ({
compact={false}
disabled={readOnly}
onChange={(values) => handleChange(values)}
legend={labelText}
legend={overrideDisplay?.renderLegend === false ? null : labelText}
description={textResourceBindings?.description && getTextResource(textResourceBindings.description)}
error={!isValid}
fieldSetProps={{
'aria-label': overrideDisplay?.renderedInTable
? getTextResourceAsString(textResourceBindings?.title)
: undefined,
}}
helpText={textResourceBindings?.help && getTextResource(textResourceBindings.help)}
variant={
shouldUseRowLayout({
Expand All @@ -131,9 +140,10 @@ export const CheckboxContainerComponent = ({
name: option.value,
checkboxId: `${id}-${option.label.replace(/\s/g, '-')}`,
checked: selected.includes(option.value),
label: getTextResource(option.label),
hideLabel,
label: hideLabel ? getTextResourceAsString(option.label) : getTextResource(option.label),
description: getTextResource(option.description),
helpText: getTextResource(option.helpText),
helpText: option.helpText && getTextResource(option.helpText),
}))}
/>
</div>
Expand Down
5 changes: 2 additions & 3 deletions src/layout/Checkboxes/MultipleChoiceSummary.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from 'react';

import { render as rtlRender } from '@testing-library/react';

import { MultipleChoiceSummary } from 'src/layout/Checkboxes/MultipleChoiceSummary';
import { renderWithProviders } from 'src/testUtils';
import type { IMultipleChoiceSummaryProps } from 'src/layout/Checkboxes/MultipleChoiceSummary';

describe('MultipleChoiceSummary', () => {
Expand All @@ -17,7 +16,7 @@ function render(props: Partial<IMultipleChoiceSummaryProps> = {}) {
formData: { 'some-key': 'This is a text', 'some-other-key': 'This is another text' },
};

return rtlRender(
return renderWithProviders(
<MultipleChoiceSummary
{...defaultProps}
{...props}
Expand Down
48 changes: 33 additions & 15 deletions src/layout/Checkboxes/MultipleChoiceSummary.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React from 'react';

import { Grid, List, ListItem, ListItemText, makeStyles } from '@material-ui/core';
import { Grid, List, ListItem, ListItemText, makeStyles, Typography } from '@material-ui/core';

import { useAppSelector } from 'src/hooks/useAppSelector';
import { getLanguageFromKey } from 'src/language/sharedLanguage';

export interface IMultipleChoiceSummaryProps {
formData: { [key: string]: string };
Expand All @@ -22,31 +25,46 @@ const useStyles = makeStyles({
fontSize: '1.125rem',
},
},
emptyField: {
fontStyle: 'italic',
fontSize: '1rem',
lineHeight: 1.6875,
},
});

export function MultipleChoiceSummary({ formData }: IMultipleChoiceSummaryProps) {
const classes = useStyles();
const language = useAppSelector((state) => state.language.language);

return (
<Grid
item
xs={12}
data-testid={'multiple-choice-summary'}
>
<List classes={{ root: classes.list }}>
{Object.keys(formData).map((key) => (
<ListItem
key={key}
classes={{ root: classes.listItem }}
>
<ListItemText
id={key}
primaryTypographyProps={{ classes: { root: classes.data } }}
primary={formData[key]}
/>
</ListItem>
))}
</List>
{Object.keys(formData).length === 0 ? (
<Typography
variant='body1'
className={classes.emptyField}
>
{getLanguageFromKey('general.empty_summary', language || {})}
</Typography>
) : (
<List classes={{ root: classes.list }}>
{Object.keys(formData).map((key) => (
<ListItem
key={key}
classes={{ root: classes.listItem }}
>
<ListItemText
id={key}
primaryTypographyProps={{ classes: { root: classes.data } }}
primary={formData[key]}
/>
</ListItem>
))}
</List>
)}
</Grid>
);
}
15 changes: 14 additions & 1 deletion src/layout/Datepicker/DatepickerComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,15 @@ class AltinnMomentUtils extends MomentUtils {
// We dont use the built-in validation for the 3rd party component, so it is always empty string
const emptyString = '';

export function DatepickerComponent({ node, language, formData, handleDataChange, isValid }: IDatepickerProps) {
export function DatepickerComponent({
node,
language,
formData,
handleDataChange,
isValid,
overrideDisplay,
getTextResourceAsString,
}: IDatepickerProps) {
const classes = useStyles();
const { minDate, maxDate, format, timeStamp = true, readOnly, required, id, textResourceBindings } = node.item;

Expand Down Expand Up @@ -183,6 +191,11 @@ export function DatepickerComponent({ node, language, formData, handleDataChange
'aria-describedby': `description-${id}`,
}),
}}
inputProps={{
'aria-label': overrideDisplay?.renderedInTable
? getTextResourceAsString(textResourceBindings?.title)
: undefined,
}}
DialogProps={{ className: classes.dialog }}
PopoverProps={{ className: classes.dialog }}
FormHelperTextProps={{
Expand Down
6 changes: 5 additions & 1 deletion src/layout/Dropdown/DropdownComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ export function DropdownComponent({
formData,
handleDataChange,
isValid,
overrideDisplay,
getTextResourceAsString,
}: IDropdownProps) {
const { optionsId, preselectedOptionIndex, id, readOnly, mapping, source } = node.item;
const { optionsId, preselectedOptionIndex, id, readOnly, mapping, source, textResourceBindings } = node.item;
const language = useAppSelector((state) => state.language.language);
const textResources = useAppSelector((state) => state.textResources.resources);
const options = useGetOptions({ optionsId, mapping, source })?.filter(duplicateOptionFilter);
Expand Down Expand Up @@ -75,6 +76,9 @@ export function DropdownComponent({
value: option.value,
})) || []
}
aria-label={
overrideDisplay?.renderedInTable ? getTextResourceAsString(textResourceBindings?.title) : undefined
}
/>
)}
</>
Expand Down
4 changes: 4 additions & 0 deletions src/layout/FileUpload/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ export class FileUpload extends FormComponent<'FileUpload'> {
renderSummary({ targetNode }: SummaryRendererProps<'FileUpload'>): JSX.Element | null {
return <AttachmentSummaryComponent targetNode={targetNode} />;
}

canRenderInTable(): boolean {
return false;
}
}
4 changes: 4 additions & 0 deletions src/layout/FileUploadWithTag/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ export class FileUploadWithTag extends FormComponent<'FileUploadWithTag'> {
renderSummary({ targetNode }: SummaryRendererProps<'FileUploadWithTag'>): JSX.Element | null {
return <AttachmentWithTagSummaryComponent targetNode={targetNode} />;
}

canRenderInTable(): boolean {
return false;
}
}
Loading