Skip to content

Commit

Permalink
simplify studioToggleableTextfield some
Browse files Browse the repository at this point in the history
  • Loading branch information
standeren committed Jan 10, 2025
1 parent 683992b commit fd87234
Show file tree
Hide file tree
Showing 43 changed files with 402 additions and 662 deletions.
5 changes: 1 addition & 4 deletions frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"app_content_library.code_lists.code_list_accordion_title": "Kodeliste: {{codeListTitle}}",
"app_content_library.code_lists.code_list_edit_id_label": "Navn på kodeliste",
"app_content_library.code_lists.code_list_edit_id_title": "Rediger navn på kodelisten {{codeListName}}",
"app_content_library.code_lists.code_list_view_id_title": "Navn på kodeliste: {{codeListName}}",
"app_content_library.code_lists.code_lists_count_info_none": "Det finnes ingen kodelister i biblioteket.",
"app_content_library.code_lists.code_lists_count_info_plural": "Det finnes <bold>{{codeListsCount}}</bold> kodelister i biblioteket.",
"app_content_library.code_lists.code_lists_count_info_single": "Det finnes <bold>1</bold> kodeliste i biblioteket.",
Expand Down Expand Up @@ -689,7 +688,7 @@
"process_editor.configuration_panel_actions_set_server_action_info": "Angir at handlingen knyttes til neste steg i prosessen.",
"process_editor.configuration_panel_actions_set_server_action_label": "Knytt handlingen til neste steg.",
"process_editor.configuration_panel_actions_title": "Handlinger",
"process_editor.configuration_panel_change_task_id": "Endre ID",
"process_editor.configuration_panel_change_task_id_label": "Oppgave-ID",
"process_editor.configuration_panel_confirmation_task": "Oppgave: Bekreftelse",
"process_editor.configuration_panel_custom_receipt_accordion_header": "Kvittering",
"process_editor.configuration_panel_custom_receipt_cancel_button": "Avbryt",
Expand All @@ -703,7 +702,6 @@
"process_editor.configuration_panel_custom_receipt_delete_receipt": "Er du sikker på at du vil slette kvitteringen din?",
"process_editor.configuration_panel_custom_receipt_heading": "Opprett din egen kvittering",
"process_editor.configuration_panel_custom_receipt_info": "Hvis du heller vil lage din egen kvittering, kan du opprette den her. Kvitteringen du lager selv vil overstyre standardkvitteringen.",
"process_editor.configuration_panel_custom_receipt_layout_set_name": "Navn på kvittering: ",
"process_editor.configuration_panel_custom_receipt_layout_set_name_validation": "Navnet må ha minst 2 tegn",
"process_editor.configuration_panel_custom_receipt_navigate_to_design_button": "Gå til Utforming",
"process_editor.configuration_panel_custom_receipt_navigate_to_design_title": "Gå til Utforming for å utforme kvitteringen din",
Expand Down Expand Up @@ -1553,7 +1551,6 @@
"ux_editor.file_upload_component.valid_file_endings": "Innstillinger for filopplastingskomponent",
"ux_editor.formLayout.warning_duplicates": "Du har den samme ID-en på flere komponenter: ",
"ux_editor.formLayout.warning_duplicates.cannot_publish": "Du kan ikke publisere appen eller konfigurere komponentene før du har rettet opp feilen.",
"ux_editor.id_identifier": "ID: {{item}}",
"ux_editor.image_component.settings": "Innstillinger for bilde",
"ux_editor.info": "Informasjon",
"ux_editor.input_popover_label": "Gi nytt navn til siden",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
.container {
display: flex;
gap: var(--fds-spacing-2);
padding: var(--fds-spacing-3);
box-sizing: border-box;
align-items: flex-start;
padding: var(--fds-spacing-2) var(--fds-spacing-2);
}

.prefixIcon {
color: var(--fds-semantic-text-neutral-default);
margin-top: var(--fds-spacing-7);
margin-top: var(--fds-spacing-10);
font-size: var(--fds-sizing-4);
align-content: center;
}

.textfield {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default meta;
export const Preview: Story = (args) => <StudioIconTextfield {...args}></StudioIconTextfield>;

Preview.args = {
icon: <PencilIcon />,
Icon: PencilIcon,
value: 2.3,
error: 'Your custom error message!',
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,38 @@ import { testCustomAttributes } from '../../test-utils/testCustomAttributes';

describe('StudioIconTextfield', () => {
it('render the icon', async () => {
renderStudioIconTextfield({
icon: <KeyVerticalIcon title='my key icon title' />,
});
expect(screen.getByTitle('my key icon title')).toBeInTheDocument();
renderStudioIconTextfield();
expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument();
});

it('should render label', () => {
renderStudioIconTextfield({
icon: <div />,
label: 'id',
});
expect(screen.getByLabelText('id')).toBeInTheDocument();
renderStudioIconTextfield();
expect(screen.getByLabelText(label)).toBeInTheDocument();
});

it('should execute onChange callback when input value changes', async () => {
const user = userEvent.setup();
const onChangeMock = jest.fn();

renderStudioIconTextfield({
icon: <div />,
label: 'Your ID',
onChange: onChangeMock,
});

const input = screen.getByLabelText('Your ID');

renderStudioIconTextfield();
const input = screen.getByRole('textbox', { name: label });
const inputValue = 'my id is 123';
await user.type(input, inputValue);
expect(onChangeMock).toHaveBeenCalledTimes(inputValue.length);
expect(onChange).toHaveBeenCalledTimes(inputValue.length);
});

it('should forward the rest of the props to the input', () => {
const getTextbox = (): HTMLInputElement => screen.getByRole('textbox') as HTMLInputElement;
testCustomAttributes<HTMLInputElement>(renderStudioIconTextfield, getTextbox);
});
});
const renderStudioIconTextfield = (props: StudioIconTextfieldProps) => {
return render(<StudioIconTextfield {...props} />);

const label = 'label';
const onChange = jest.fn();
const defaultProps: StudioIconTextfieldProps = {
Icon: KeyVerticalIcon,
label,
onChange,
};

const renderStudioIconTextfield = (props: Partial<StudioIconTextfieldProps> = {}) => {
return render(<StudioIconTextfield {...defaultProps} {...props} />);
};
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import React, { forwardRef } from 'react';
import { StudioTextfield, type StudioTextfieldProps } from '../StudioTextfield';
import cn from 'classnames';

import classes from './StudioIconTextfield.module.css';

export type StudioIconTextfieldProps = {
icon: React.ReactNode;
Icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>;
} & StudioTextfieldProps;

export const StudioIconTextfield = forwardRef<HTMLDivElement, StudioIconTextfieldProps>(
(
{ icon, className: givenClassName, ...rest }: StudioIconTextfieldProps,
{ Icon, className: givenClassName, ...rest }: StudioIconTextfieldProps,
ref,
): React.ReactElement => {
const className = cn(givenClassName, classes.container);
return (
<div className={className} ref={ref}>
<div aria-hidden className={classes.prefixIcon}>
{icon}
</div>
<StudioTextfield {...rest} className={classes.textfield} />
<Icon aria-hidden className={classes.prefixIcon} />
<StudioTextfield size='small' {...rest} className={classes.textfield} />
</div>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const ExampleUseCase: ExampleUseCase = (args): React.ReactElement => {
<StudioIconTextfield
error={name !== 'Bernard' ? 'Navnet må være Bernard' : ''}
onChange={(e) => setName(e.target.value)}
icon={<KeyVerticalIcon />}
Icon={KeyVerticalIcon}
size='sm'
label='Nytt navn'
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
grid-template-columns: auto 1fr;
text-align: left;
align-items: center;
justify-content: space-between;
color: var(--fds-semantic-text-neutral-default);
width: 100%;
gap: var(--fds-spacing-1);
Expand All @@ -33,18 +34,12 @@
flex-direction: column;
}

.editIconWrapper {
flex: 1;
text-align: right;
.editIcon {
display: none;
}

.button:hover .editIconWrapper,
.button:focus .editIconWrapper {
.button:hover .editIcon,
.button:focus .editIcon {
display: flex;
align-items: center;
}

.editIcon {
margin-left: auto;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,47 @@ import { render, screen } from '@testing-library/react';
import { StudioTextfieldToggleView } from './StudioTextfieldToggleView';
import type { StudioTextfieldToggleViewProps } from './StudioTextfieldToggleView';
import userEvent from '@testing-library/user-event';
import { KeyVerticalIcon } from '@studio/icons';

describe('StudioTextfieldToggleView', () => {
it('should render button text', () => {
renderStudioTextfieldToggleView({ children: 'My awesome button' });
expect(screen.getByRole('button', { name: 'My awesome button' })).toBeInTheDocument();
renderStudioTextfieldToggleView();
expect(screen.getByRole('button', { name: value })).toBeInTheDocument();
});

it('should execute the "onClick" method when button is clicked', async () => {
const user = userEvent.setup();
const onClickMock = jest.fn();

renderStudioTextfieldToggleView({ children: 'My awesome button text', onClick: onClickMock });

await user.click(screen.getByRole('button', { name: 'My awesome button text' }));
expect(onClickMock).toHaveBeenCalledTimes(1);
});

it('should render the KeyVerticalIcon', () => {
renderStudioTextfieldToggleView({ children: 'My awesome button text' });

// Uses testId to find the KeyVerticalIcon, since it's not available for screen reader.
expect(screen.getByTestId('keyIcon')).toBeInTheDocument();
renderStudioTextfieldToggleView();
await user.click(screen.getByRole('button', { name: value }));
expect(onClick).toHaveBeenCalledTimes(1);
});

it('should render the PencilIcon', () => {
renderStudioTextfieldToggleView({ children: 'My awesome button text' });

// Uses testId to find the EditIcon, since it's not available for screen reader.
expect(screen.getByTestId('editIcon')).toBeInTheDocument();
it('should render the both given Icon and pencilIcon', () => {
renderStudioTextfieldToggleView();
expect(screen.getAllByRole('img', { hidden: true })).toHaveLength(2);
});

it('should forward the rest of the props to the button', () => {
renderStudioTextfieldToggleView({ children: 'My awesome button text', disabled: true });
expect(screen.getByRole('button', { name: 'My awesome button text' })).toBeDisabled();
renderStudioTextfieldToggleView({ disabled: true });
expect(screen.getByRole('button', { name: value })).toBeDisabled();
});

it('should show label if defined', () => {
const studioTextfieldToggleViewLabel = 'studioTextfieldToggleViewLabel';
renderStudioTextfieldToggleView({
children: 'My awesome button text',
label: studioTextfieldToggleViewLabel,
});
expect(screen.getByText(studioTextfieldToggleViewLabel)).toBeInTheDocument();
renderStudioTextfieldToggleView();
expect(screen.getByText(label)).toBeInTheDocument();
});
});

const renderStudioTextfieldToggleView = (props: Partial<StudioTextfieldToggleViewProps>) => {
return render(<StudioTextfieldToggleView {...props} />);
const value = 'value';
const label = 'label';
const onClick = jest.fn();
const defaultProps: StudioTextfieldToggleViewProps = {
value,
label,
onClick,
Icon: KeyVerticalIcon,
};

const renderStudioTextfieldToggleView = (props: Partial<StudioTextfieldToggleViewProps> = {}) => {
return render(<StudioTextfieldToggleView {...defaultProps} {...props} />);
};
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
import React from 'react';
import { PencilIcon, KeyVerticalIcon } from '@studio/icons';
import { PencilIcon } from '@studio/icons';
import { StudioButton, type StudioButtonProps } from '@studio/components';
import classes from './StudioTextfieldToggleView.module.css';
import cn from 'classnames';

export type StudioTextfieldToggleViewProps = StudioButtonProps & {
export type StudioTextfieldToggleViewProps = Omit<StudioButtonProps, 'icon'> & {
Icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>;
label?: string;
};

export const StudioTextfieldToggleView = ({
onClick,
children,
title,
label,
className: givenClass,
icon = <KeyVerticalIcon data-testid='keyIcon' aria-hidden />,
Icon,
...rest
}: StudioTextfieldToggleViewProps) => {
const className = cn(classes.button, givenClass);

return (
<StudioButton className={className} onClick={onClick} {...rest}>
<span className={classes.viewModeIconsContainer} title={title}>
{icon}
<StudioButton variant='tertiary' className={className} onClick={onClick} {...rest}>
<span className={classes.viewModeIconsContainer}>
<Icon aria-hidden />
<span className={classes.textContainer}>
{label && <span className={classes.label}>{label}</span>}
<span className={classes.ellipsis}>{children}</span>
{label && (
<span className={classes.label} aria-hidden>
{label}
</span>
)}
<span className={classes.ellipsis}>{rest.value}</span>
</span>
</span>
<span className={classes.editIconWrapper}>
<PencilIcon className={classes.editIcon} data-testid='editIcon' aria-hidden />
</span>
<PencilIcon className={classes.editIcon} aria-hidden />
</StudioButton>
);
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import type { Meta, StoryFn } from '@storybook/react';
import { StudioToggleableTextfield } from './StudioToggleableTextfield';
import { KeyVerticalIcon } from '@studio/icons';

type Story = StoryFn<typeof StudioToggleableTextfield>;

Expand All @@ -16,19 +15,10 @@ export const Preview: Story = (args) => (

Preview.args = {
onIsViewMode: () => {},
viewProps: {
variant: 'tertiary',
size: 'small',
label: 'My awesome label',
children: 'My awesome value',
},
inputProps: {
icon: <KeyVerticalIcon />,
label: 'My awesome label',
size: 'small',
placeholder: 'Placeholder',
error: '',
},
label: 'My awesome label',
title: 'My awesome title',
value: 'My awesome value',
error: '',
};

export default meta;
Loading

0 comments on commit fd87234

Please sign in to comment.