From a18f8f28803bd00dd82bccf29933854e273c9da8 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Thu, 15 Jan 2026 09:10:43 +0100 Subject: [PATCH 1/4] Added tableTitle and tertiary to custom button --- src/layout/CustomButton/CustomButtonComponent.tsx | 1 + src/layout/CustomButton/config.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/layout/CustomButton/CustomButtonComponent.tsx b/src/layout/CustomButton/CustomButtonComponent.tsx index 6e3b23b993..0729f088dc 100644 --- a/src/layout/CustomButton/CustomButtonComponent.tsx +++ b/src/layout/CustomButton/CustomButtonComponent.tsx @@ -175,6 +175,7 @@ function useHandleServerActionMutationFn(acquireLock: FormDataLocking) { export const buttonStyles: { [style in CBTypes.ButtonStyle]: { color: ButtonColor; variant: ButtonVariant } } = { primary: { variant: 'primary', color: 'success' }, secondary: { variant: 'secondary', color: 'first' }, + tertiary: { variant: 'tertiary', color: 'second' }, }; function toShorthandSize(size?: CBTypes.CustomButtonSize): 'sm' | 'md' | 'lg' { diff --git a/src/layout/CustomButton/config.ts b/src/layout/CustomButton/config.ts index 16c553d27d..dc2065607d 100644 --- a/src/layout/CustomButton/config.ts +++ b/src/layout/CustomButton/config.ts @@ -85,4 +85,11 @@ export const Config = new CG.component({ .exportAs('CustomButtonSize'), ), ) - .addTextResource(new CG.trb({ name: 'title', title: 'Title', description: 'The title/text on the button' })); + .addTextResource(new CG.trb({ name: 'title', title: 'Title', description: 'The title/text on the button' })) + .addTextResource( + new CG.trb({ + name: 'tableTitle', + title: 'Table title', + description: 'The title/text for the button when rendered in a table', + }), + ); From 80702d38afbd3aea85e2a68b78c9fbc5073daa11 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Thu, 15 Jan 2026 10:14:44 +0100 Subject: [PATCH 2/4] refactor --- src/layout/CustomButton/CustomButtonComponent.tsx | 11 +++++++++-- src/layout/CustomButton/config.ts | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/layout/CustomButton/CustomButtonComponent.tsx b/src/layout/CustomButton/CustomButtonComponent.tsx index 0729f088dc..235ba4f7f4 100644 --- a/src/layout/CustomButton/CustomButtonComponent.tsx +++ b/src/layout/CustomButton/CustomButtonComponent.tsx @@ -194,7 +194,10 @@ function toShorthandSize(size?: CBTypes.CustomButtonSize): 'sm' | 'md' | 'lg' { } } -export const CustomButtonComponent = ({ baseComponentId }: PropsFromGenericComponent<'CustomButton'>) => { +export const CustomButtonComponent = ({ + baseComponentId, + overrideDisplay, +}: PropsFromGenericComponent<'CustomButton'>) => { const { textResourceBindings, actions, id, buttonColor, buttonSize, buttonStyle } = useItemWhenType( baseComponentId, 'CustomButton', @@ -229,7 +232,11 @@ export const CustomButtonComponent = ({ baseComponentId }: PropsFromGenericCompo interceptedButtonStyle = 'primary'; } - let buttonText = textResourceBindings?.title; + const isInTable = overrideDisplay?.renderedInTable === true; + + let buttonText = isInTable + ? (textResourceBindings?.tableTitle ?? textResourceBindings?.title) + : textResourceBindings?.title; if (isSubformCloseButton && !buttonText) { buttonText = 'general.done'; } diff --git a/src/layout/CustomButton/config.ts b/src/layout/CustomButton/config.ts index dc2065607d..6c233ad873 100644 --- a/src/layout/CustomButton/config.ts +++ b/src/layout/CustomButton/config.ts @@ -58,7 +58,7 @@ export const Config = new CG.component({ .addProperty( new CG.prop( 'buttonStyle', - new CG.enum('primary', 'secondary') + new CG.enum('primary', 'secondary', 'tertiary') .setTitle('Button style') .setDescription('The style/color scheme of the button.') .optional({ default: 'secondary' }) From 273d50340d4f3c01efdb3fc1b1aa6323b826f516 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Thu, 15 Jan 2026 10:58:38 +0100 Subject: [PATCH 3/4] added test for rendering tabeTitle --- .../CustomButtonComponent.test.tsx | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/layout/CustomButton/CustomButtonComponent.test.tsx b/src/layout/CustomButton/CustomButtonComponent.test.tsx index 9c215779b3..897014a06a 100644 --- a/src/layout/CustomButton/CustomButtonComponent.test.tsx +++ b/src/layout/CustomButton/CustomButtonComponent.test.tsx @@ -6,6 +6,7 @@ import { screen } from '@testing-library/react'; import { CustomButtonComponent } from 'src/layout/CustomButton/CustomButtonComponent'; import { fetchProcessState } from 'src/queries/queries'; import { renderGenericComponentTest } from 'src/test/renderWithProviders'; +import type { PropsFromGenericComponent } from 'src/layout'; import type { CustomAction } from 'src/layout/CustomButton/config.generated'; import type { IUserAction } from 'src/types/shared'; @@ -117,14 +118,46 @@ describe('CustomButtonComponent', () => { expect(screen.getByRole('button')).not.toBeDisabled(); }); + + it('uses tableTitle when rendered in a table', async () => { + await render({ + actions: [{ id: 'nextPage', type: 'ClientAction' }], + textResourceBindingsOverride: { + title: 'Custom button', + tableTitle: 'Custom table button', + }, + overrideDisplay: { renderedInTable: true }, + }); + expect(screen.getByRole('button', { name: 'Custom table button' })).toBeInTheDocument(); + }); + + it('falls back to title when tableTitle is missing in a table', async () => { + await render({ + actions: [{ id: 'nextPage', type: 'ClientAction' }], + textResourceBindingsOverride: { + title: 'Custom button', + }, + overrideDisplay: { renderedInTable: true }, + }); + expect(screen.getByRole('button', { name: 'Custom button' })).toBeInTheDocument(); + }); }); type RenderProps = { actions?: CustomAction[]; actionAuthorization?: IUserAction[]; + textResourceBindingsOverride?: { + title?: string; + tableTitle?: string; + }; + overrideDisplay?: PropsFromGenericComponent<'CustomButton'>['overrideDisplay']; }; -async function render({ actions, actionAuthorization }: RenderProps = { actionAuthorization: [] }) { +async function render( + { actions, actionAuthorization, textResourceBindingsOverride, overrideDisplay }: RenderProps = { + actionAuthorization: [], + }, +) { jest.mocked(fetchProcessState).mockImplementation(async () => ({ started: '2024-01-03T06:52:49.716640678Z', ended: null, @@ -169,8 +202,12 @@ async function render({ actions, actionAuthorization }: RenderProps = { actionAu component: { textResourceBindings: { title: 'Custom button', + ...textResourceBindingsOverride, }, - actions, + actions: actions ?? [], + }, + genericProps: { + overrideDisplay, }, }); } From b248c99e5ddf9502246751858b6cb21221a7f4e1 Mon Sep 17 00:00:00 2001 From: Jamal Alabdullah Date: Tue, 20 Jan 2026 12:09:38 +0100 Subject: [PATCH 4/4] refactor --- .../CustomButtonComponent.test.tsx | 41 +------------------ .../CustomButton/CustomButtonComponent.tsx | 11 +---- 2 files changed, 4 insertions(+), 48 deletions(-) diff --git a/src/layout/CustomButton/CustomButtonComponent.test.tsx b/src/layout/CustomButton/CustomButtonComponent.test.tsx index 897014a06a..9c215779b3 100644 --- a/src/layout/CustomButton/CustomButtonComponent.test.tsx +++ b/src/layout/CustomButton/CustomButtonComponent.test.tsx @@ -6,7 +6,6 @@ import { screen } from '@testing-library/react'; import { CustomButtonComponent } from 'src/layout/CustomButton/CustomButtonComponent'; import { fetchProcessState } from 'src/queries/queries'; import { renderGenericComponentTest } from 'src/test/renderWithProviders'; -import type { PropsFromGenericComponent } from 'src/layout'; import type { CustomAction } from 'src/layout/CustomButton/config.generated'; import type { IUserAction } from 'src/types/shared'; @@ -118,46 +117,14 @@ describe('CustomButtonComponent', () => { expect(screen.getByRole('button')).not.toBeDisabled(); }); - - it('uses tableTitle when rendered in a table', async () => { - await render({ - actions: [{ id: 'nextPage', type: 'ClientAction' }], - textResourceBindingsOverride: { - title: 'Custom button', - tableTitle: 'Custom table button', - }, - overrideDisplay: { renderedInTable: true }, - }); - expect(screen.getByRole('button', { name: 'Custom table button' })).toBeInTheDocument(); - }); - - it('falls back to title when tableTitle is missing in a table', async () => { - await render({ - actions: [{ id: 'nextPage', type: 'ClientAction' }], - textResourceBindingsOverride: { - title: 'Custom button', - }, - overrideDisplay: { renderedInTable: true }, - }); - expect(screen.getByRole('button', { name: 'Custom button' })).toBeInTheDocument(); - }); }); type RenderProps = { actions?: CustomAction[]; actionAuthorization?: IUserAction[]; - textResourceBindingsOverride?: { - title?: string; - tableTitle?: string; - }; - overrideDisplay?: PropsFromGenericComponent<'CustomButton'>['overrideDisplay']; }; -async function render( - { actions, actionAuthorization, textResourceBindingsOverride, overrideDisplay }: RenderProps = { - actionAuthorization: [], - }, -) { +async function render({ actions, actionAuthorization }: RenderProps = { actionAuthorization: [] }) { jest.mocked(fetchProcessState).mockImplementation(async () => ({ started: '2024-01-03T06:52:49.716640678Z', ended: null, @@ -202,12 +169,8 @@ async function render( component: { textResourceBindings: { title: 'Custom button', - ...textResourceBindingsOverride, }, - actions: actions ?? [], - }, - genericProps: { - overrideDisplay, + actions, }, }); } diff --git a/src/layout/CustomButton/CustomButtonComponent.tsx b/src/layout/CustomButton/CustomButtonComponent.tsx index 235ba4f7f4..0729f088dc 100644 --- a/src/layout/CustomButton/CustomButtonComponent.tsx +++ b/src/layout/CustomButton/CustomButtonComponent.tsx @@ -194,10 +194,7 @@ function toShorthandSize(size?: CBTypes.CustomButtonSize): 'sm' | 'md' | 'lg' { } } -export const CustomButtonComponent = ({ - baseComponentId, - overrideDisplay, -}: PropsFromGenericComponent<'CustomButton'>) => { +export const CustomButtonComponent = ({ baseComponentId }: PropsFromGenericComponent<'CustomButton'>) => { const { textResourceBindings, actions, id, buttonColor, buttonSize, buttonStyle } = useItemWhenType( baseComponentId, 'CustomButton', @@ -232,11 +229,7 @@ export const CustomButtonComponent = ({ interceptedButtonStyle = 'primary'; } - const isInTable = overrideDisplay?.renderedInTable === true; - - let buttonText = isInTable - ? (textResourceBindings?.tableTitle ?? textResourceBindings?.title) - : textResourceBindings?.title; + let buttonText = textResourceBindings?.title; if (isSubformCloseButton && !buttonText) { buttonText = 'general.done'; }