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

feat(subform): tablecolumns config design update #13902

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
da0f448
Update tableColumns ux
Jondyr Oct 22, 2024
fb636fa
Use StudioProperty.Button for consistency
Jondyr Oct 23, 2024
f14710b
Update tests for ColumnElement
Jondyr Oct 24, 2024
315cfc1
Add new mock file for subform layouts
Jondyr Oct 24, 2024
ae271ea
Remove unnecessary var
Jondyr Oct 24, 2024
ee66cc8
Add test case for text key display value lookup
Jondyr Oct 24, 2024
0cd9ef8
Remove unused import
Jondyr Oct 24, 2024
d7f4015
Merge branch 'main' into 13683-update-subform-tablecolumns-config-to-…
Jondyr Oct 28, 2024
2b3777f
Fix css issue
Jondyr Oct 28, 2024
2a339f9
Use textresourceselector
Jondyr Oct 28, 2024
46ada61
Fix missing import
Jondyr Oct 28, 2024
a5b5cea
Remove unused css
Jondyr Oct 28, 2024
d3d9630
Rename some cases of "SubForm" to "Subform"
Jondyr Oct 28, 2024
6d5c2a5
Fix wrong css module import name
Jondyr Oct 28, 2024
e8bba90
Add click handler function
Jondyr Oct 29, 2024
6f519d3
Move EditColumnElement to new folder
Jondyr Oct 29, 2024
6e3c159
Formatting
Jondyr Oct 29, 2024
e9423e8
Fix styling for column editing card
Jondyr Oct 29, 2024
03e715e
Merge branch 'main' into 13683-update-subform-tablecolumns-config-to-…
Jondyr Oct 29, 2024
54f56c1
Fix Subform naming
Jondyr Oct 29, 2024
4a0fee1
Merge branch 'main' into 13683-update-subform-tablecolumns-config-to-…
Jondyr Oct 29, 2024
469d9bd
Merge branch 'main' into 13683-update-subform-tablecolumns-config-to-…
Jondyr Oct 29, 2024
e337aa4
Merge branch 'main' into 13683-update-subform-tablecolumns-config-to-…
Jondyr Oct 30, 2024
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
1 change: 1 addition & 0 deletions frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,7 @@
"ux_editor.properties_panel.subform_table_columns.add_column": "Legg til kolonne",
"ux_editor.properties_panel.subform_table_columns.cell_content_default_label": "Default",
"ux_editor.properties_panel.subform_table_columns.cell_content_query_label": "Query",
"ux_editor.properties_panel.subform_table_columns.choose_component": "Velg komponenten fra underskjemaet som skal vises her",
"ux_editor.properties_panel.subform_table_columns.column_header": "Kolonne {{columnNumber}}",
"ux_editor.properties_panel.subform_table_columns.delete_column": "Slett kolonne {{columnNumber}}",
"ux_editor.properties_panel.subform_table_columns.header_content_label": "Header Content",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.wrapper {
margin-top: var(--fds-spacing-4);
margin-top: var(--fds-spacing-1);
}

.headerWrapper {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import React from 'react';
import { screen } from '@testing-library/react';
import { screen, waitFor } from '@testing-library/react';
import { ColumnElement, type ColumnElementProps } from './ColumnElement';
import { textMock } from '@studio/testing/mocks/i18nMock';
import { renderWithProviders } from 'dashboard/testing/mocks';
import { createQueryClientMock } from 'app-shared/mocks/queryClientMock';
import { queriesMock } from 'app-shared/mocks/queriesMock';
import userEvent from '@testing-library/user-event';
import { type TableColumn } from '../types/TableColumn';
import { layoutSet3SubformNameMock } from '../../../../testing/layoutSetsMock';
import { QueryKey } from 'app-shared/types/QueryKey';
import { app, org } from '@studio/testing/testids';
import { subformLayoutMock } from '../../../../testing/subformLayoutMock';

const headerContentMock: string = 'Header';
const cellContentQueryMock: string = 'Query';
Expand All @@ -26,6 +30,7 @@ const defaultProps: ColumnElementProps = {
columnNumber: columnNumberMock,
onDeleteColumn: jest.fn(),
onEdit: jest.fn(),
layoutSetName: layoutSet3SubformNameMock,
};

describe('ColumnElement', () => {
Expand All @@ -41,79 +46,33 @@ describe('ColumnElement', () => {
onEdit: onEditMock,
});

const headerInputbutton = screen.getByRole('button', {
name: `${textMock('ux_editor.properties_panel.subform_table_columns.header_content_label')}: ${headerContentMock}`,
const editButton = screen.getByRole('button', {
name: /ux_editor.properties_panel.subform_table_columns.column_header/,
});
await user.click(headerInputbutton);
await user.click(editButton);

const headerInputfield = screen.getByLabelText(
textMock('ux_editor.properties_panel.subform_table_columns.header_content_label'),
);
const newValue: string = 'a';
await user.type(headerInputfield, newValue);
await user.tab();

expect(onEditMock).toHaveBeenCalledTimes(1);
expect(onEditMock).toHaveBeenCalledWith({
...mockTableColumn,
headerContent: `${headerContentMock}${newValue}`,
});
});

it('should call onEdit with updated query content when query text field is blurred', async () => {
const onEditMock = jest.fn();

const user = userEvent.setup();
renderColumnElement({
onEdit: onEditMock,
});

const queryInputbutton = screen.getByRole('button', {
name: `${textMock('ux_editor.properties_panel.subform_table_columns.cell_content_query_label')}: ${cellContentQueryMock}`,
const componentSelect = screen.getByRole('combobox', {
name: textMock('ux_editor.properties_panel.subform_table_columns.choose_component'),
});
await user.click(queryInputbutton);

const queryInputfield = screen.getByLabelText(
textMock('ux_editor.properties_panel.subform_table_columns.cell_content_query_label'),
await user.click(componentSelect);
await user.click(
screen.getByRole('option', { name: new RegExp(`${subformLayoutMock.component1Id}`) }),
);
const newValue: string = 'a';
await user.type(queryInputfield, newValue);
await user.tab();

expect(onEditMock).toHaveBeenCalledTimes(1);
expect(onEditMock).toHaveBeenCalledWith({
...mockTableColumn,
cellContent: { ...mockTableColumn.cellContent, query: `${cellContentQueryMock}${newValue}` },
await waitFor(async () => {
await user.click(
screen.getByRole('button', {
name: textMock('general.save'),
}),
);
});
});

it('should call onEdit with updated default content when default text field is blurred', async () => {
const onEditMock = jest.fn();

const user = userEvent.setup();
renderColumnElement({
onEdit: onEditMock,
});

const defaultInputbutton = screen.getByRole('button', {
name: `${textMock('ux_editor.properties_panel.subform_table_columns.cell_content_default_label')}: ${cellContentDefaultMock}`,
});
await user.click(defaultInputbutton);

const defaultInputfield = screen.getByLabelText(
textMock('ux_editor.properties_panel.subform_table_columns.cell_content_default_label'),
);
const newValue: string = 'a';
await user.type(defaultInputfield, newValue);
await user.tab();

expect(onEditMock).toHaveBeenCalledTimes(1);
expect(onEditMock).toHaveBeenCalledWith({
...mockTableColumn,
cellContent: {
...mockTableColumn.cellContent,
default: `${cellContentDefaultMock}${newValue}`,
},
headerContent: subformLayoutMock.component1.textResourceBindings.title,
cellContent: { query: subformLayoutMock.component1.dataModelBindings.simpleBinding },
});
});

Expand All @@ -125,10 +84,12 @@ describe('ColumnElement', () => {
onDeleteColumn: onDeleteColumnMock,
});

const editButton = screen.getByRole('button', {
name: /ux_editor.properties_panel.subform_table_columns.column_header/,
});
await user.click(editButton);
const deleteButton = screen.getByRole('button', {
name: textMock('ux_editor.properties_panel.subform_table_columns.delete_column', {
columnNumber: columnNumberMock,
}),
name: textMock('general.delete'),
});
await user.click(deleteButton);

Expand All @@ -138,6 +99,10 @@ describe('ColumnElement', () => {

const renderColumnElement = (props: Partial<ColumnElementProps> = {}) => {
const queryClient = createQueryClientMock();
queryClient.setQueryData(
[QueryKey.FormLayouts, org, app, layoutSet3SubformNameMock],
subformLayoutMock.layoutSet,
);
return renderWithProviders(<ColumnElement {...defaultProps} {...props} />, {
...queriesMock,
queryClient,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, { type ReactElement, type ChangeEvent } from 'react';
import React, { useState, type ReactElement } from 'react';
import classes from './ColumnElement.module.css';
import { type TableColumn } from '../types/TableColumn';
import { useTranslation } from 'react-i18next';
import {
StudioButton,
StudioLabelAsParagraph,
StudioToggleableTextfield,
} from '@studio/components';
import { KeyVerticalFillIcon, TrashFillIcon } from '@studio/icons';
import { StudioProperty } from '@studio/components';
import { EditColumnElement } from './EditColumnElement';
import { useTextResourcesQuery } from 'app-shared/hooks/queries';
import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams';
import { textResourceByLanguageAndIdSelector } from '../../../../selectors/textResourceSelectors';

export type ColumnElementProps = {
layoutSetName: string;
tableColumn: TableColumn;
columnNumber: number;
onDeleteColumn: () => void;
Expand All @@ -21,110 +21,45 @@ export const ColumnElement = ({
columnNumber,
onDeleteColumn,
onEdit,
layoutSetName,
}: ColumnElementProps): ReactElement => {
const { t } = useTranslation();
const [editing, setEditing] = useState(false);
const { org, app } = useStudioEnvironmentParams();
const { data: textResources } = useTextResourcesQuery(org, app);

const handleEditHeaderContent = (event: ChangeEvent<HTMLInputElement>) => {
onEdit({ ...tableColumn, headerContent: event.target.value });
};

const handleEditQuery = (event: ChangeEvent<HTMLInputElement>) => {
onEdit({
...tableColumn,
cellContent: { ...tableColumn.cellContent, query: event.target.value },
});
};

const handleEditDefault = (event: ChangeEvent<HTMLInputElement>) => {
onEdit({
...tableColumn,
cellContent: { ...tableColumn.cellContent, default: event.target.value },
});
};
return (
<div className={classes.wrapper}>
<TableColumnHeader columnNumber={columnNumber} onDeleteColumn={onDeleteColumn} />
<TableColumnToggleableTextfield
label={t('ux_editor.properties_panel.subform_table_columns.header_content_label')}
value={tableColumn.headerContent}
onBlur={handleEditHeaderContent}
required={true}
/>
<TableColumnToggleableTextfield
label={t('ux_editor.properties_panel.subform_table_columns.cell_content_query_label')}
value={tableColumn.cellContent.query}
onBlur={handleEditQuery}
required={true}
/>
<TableColumnToggleableTextfield
label={t('ux_editor.properties_panel.subform_table_columns.cell_content_default_label')}
value={tableColumn.cellContent.default}
onBlur={handleEditDefault}
/>
</div>
);
};

type TableColumnHeaderProps = {
columnNumber: number;
onDeleteColumn: () => void;
};

const TableColumnHeader = ({
columnNumber,
onDeleteColumn,
}: TableColumnHeaderProps): ReactElement => {
const { t } = useTranslation();
const textKeyValue = textResourceByLanguageAndIdSelector(
'nb',
tableColumn.headerContent,
)(textResources)?.value;

return (
<div className={classes.headerWrapper}>
<StudioLabelAsParagraph size='sm'>
{t('ux_editor.properties_panel.subform_table_columns.column_header', { columnNumber })}
</StudioLabelAsParagraph>
<StudioButton
icon={<TrashFillIcon />}
title={t('ux_editor.properties_panel.subform_table_columns.delete_column', {
columnNumber,
})}
onClick={onDeleteColumn}
color='danger'
variant='secondary'
if (editing) {
return (
<EditColumnElement
layoutSetName={layoutSetName}
sourceColumn={tableColumn}
columnNumber={columnNumber}
onDeleteColumn={onDeleteColumn}
onEdit={(col) => {
setEditing(false);
onEdit(col);
}}
/>
</div>
);
};
);
}

type TableColumnToggleableTextfieldProps = {
label: string;
value: string;
onBlur: (event: ChangeEvent<HTMLInputElement>) => void;
required?: boolean;
};
const handleClickEdit = (): void => {
setEditing(true);
};

const TableColumnToggleableTextfield = ({
label,
value,
onBlur,
required = false,
}: TableColumnToggleableTextfieldProps): ReactElement => {
return (
<StudioToggleableTextfield
inputProps={{
icon: <KeyVerticalFillIcon />,
label,
value,
size: 'sm',
required,
onBlur,
}}
viewProps={{
children: (
<span>
<b>{label}:</b> {value}
</span>
),
variant: 'tertiary',
}}
/>
<StudioProperty.Button
className={classes.wrapper}
onClick={handleClickEdit}
property={t('ux_editor.properties_panel.subform_table_columns.column_header', {
columnNumber,
})}
value={textKeyValue}
></StudioProperty.Button>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.wrapper {
margin-top: var(--fds-spacing-4);
padding: var(--fds-spacing-3);
}

.header {
padding-top: var(--fds-spacing-2);
padding-bottom: var(--fds-spacing-0);
padding-left: var(--fds-spacing-0);
}

.content {
padding: var(--fds-spacing-0);
}

.divider {
margin-top: var(--fds-spacing-0);
}
.buttons {
display: flex;
gap: var(--fds-spacing-2);
}
Loading