Skip to content

Commit

Permalink
Merge branch 'main' into 13364-validation-issues-when-creating-a-new-…
Browse files Browse the repository at this point in the history
…model
  • Loading branch information
JamalAlabdullah authored Dec 5, 2024
2 parents 0eff4c7 + ea8ca6c commit a8cdd9a
Show file tree
Hide file tree
Showing 39 changed files with 968 additions and 567 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.savingContainer {
display: flex;
align-items: center;
gap: var(--fds-spacing-1);
margin-top: var(--fds-spacing-4);
}

.savedIcon {
font-size: var(--fds-sizing-8);
color: var(--fds-semantic-text-neutral-subtle);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { SaveStatus } from './SaveStatus';
import { textMock } from '@studio/testing/mocks/i18nMock';

describe('SaveStatus', () => {
it('should display a spinner while pending', () => {
render(<SaveStatus isPending isSaved={false} />);

expect(
screen.getByText(textMock('settings_modal.maskinporten_tab_save_scopes_pending')),
).toBeInTheDocument();

expect(
screen.getByTitle(textMock('settings_modal.maskinporten_tab_save_scopes_pending_spinner')),
).toBeInTheDocument();
});

it('should render saved status with checkmark icon', () => {
render(<SaveStatus isPending={false} isSaved />);

expect(
screen.getByText(textMock('settings_modal.maskinporten_tab_save_scopes_complete')),
).toBeInTheDocument();
});

it('should render nothing when neither pending nor saved', () => {
const { container } = render(<SaveStatus isPending={false} isSaved={false} />);

expect(container).toBeEmptyDOMElement();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, { type ReactElement } from 'react';
import classes from './SaveStatus.module.css';
import { StudioParagraph, StudioSpinner } from '@studio/components';
import { useTranslation } from 'react-i18next';
import { CheckmarkIcon } from '@studio/icons';

type SaveStatusProps = {
isPending: boolean;
isSaved: boolean;
};

export const SaveStatus = ({ isPending, isSaved }: SaveStatusProps): ReactElement => {
const { t } = useTranslation();

if (isPending) {
return (
<SaveStatusContent
text={t('settings_modal.maskinporten_tab_save_scopes_pending')}
isPending
/>
);
}
if (isSaved) {
return <SaveStatusContent text={t('settings_modal.maskinporten_tab_save_scopes_complete')} />;
}
return null;
};

type SaveStatusContentProps = {
text: string;
isPending?: boolean;
};

const SaveStatusContent = ({ text, isPending = false }: SaveStatusContentProps): ReactElement => {
const { t } = useTranslation();

return (
<div className={classes.savingContainer}>
<StudioParagraph size='sm'>{text}</StudioParagraph>
{isPending ? (
<StudioSpinner
spinnerTitle={t('settings_modal.maskinporten_tab_save_scopes_pending_spinner')}
size='sm'
/>
) : (
<CheckmarkIcon className={classes.savedIcon} />
)}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SaveStatus } from './SaveStatus';
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { type ChangeEvent, type ReactElement } from 'react';
import classes from './ScopeList.module.css';

import {
StudioCheckboxTable,
type StudioCheckboxTableRowElement,
Expand All @@ -25,6 +24,7 @@ import { GetInTouchWith } from 'app-shared/getInTouch';
import { EmailContactProvider } from 'app-shared/getInTouch/providers';
import { LoggedInTitle } from '../LoggedInTitle';
import { useUpdateSelectedMaskinportenScopesMutation } from 'app-development/hooks/mutations/useUpdateSelectedMaskinportenScopesMutation';
import { SaveStatus } from '../SaveStatus';

export type ScopeListProps = {
maskinPortenScopes: MaskinportenScope[];
Expand All @@ -33,8 +33,11 @@ export type ScopeListProps = {

export const ScopeList = ({ maskinPortenScopes, selectedScopes }: ScopeListProps): ReactElement => {
const { t } = useTranslation();
const { mutate: mutateSelectedMaskinportenScopes } =
useUpdateSelectedMaskinportenScopesMutation();
const {
mutate: mutateSelectedMaskinportenScopes,
isPending: isPendingSaveScopes,
isSuccess: scopesSaved,
} = useUpdateSelectedMaskinportenScopesMutation();

const checkboxTableRowElements: StudioCheckboxTableRowElement[] = mapScopesToRowElements(
maskinPortenScopes,
Expand Down Expand Up @@ -103,6 +106,7 @@ export const ScopeList = ({ maskinPortenScopes, selectedScopes }: ScopeListProps
))}
</StudioCheckboxTable.Body>
</StudioCheckboxTable>
<SaveStatus isPending={isPendingSaveScopes} isSaved={scopesSaved} />
</div>
);
};
Expand Down
8 changes: 7 additions & 1 deletion frontend/language/src/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -1024,9 +1024,12 @@
"settings_modal.maskinporten_tab_available_scopes_description_help": "Hvis du trenger tilgang til flere scopes i denne listen, <0> tar du kontakt med Altinn servicedesk.</0>",
"settings_modal.maskinporten_tab_available_scopes_description_help_link": "tar du kontakt med Altinn servicedesk.",
"settings_modal.maskinporten_tab_available_scopes_title": "Scopes for denne virksomheten",
"settings_modal.maskinporten_tab_description": "Maskinporten autentiserer og autoriserer API-er som skal brukes i apper. I Maskinporten kan du lage hvilke scopes en app skal ha fra og til andre systemer, for eksempel tilgang til persondata.",
"settings_modal.maskinporten_tab_description": "Maskinporten autentiserer og autoriserer API-er som skal brukes i apper. I Maskinporten kan du sette opp hvilke scopes en app skal ha fra og til andre systemer, for eksempel tilgang til persondata.",
"settings_modal.maskinporten_tab_login_with_ansattporten": "Logg inn med Ansattporten",
"settings_modal.maskinporten_tab_login_with_description": "Med Ansattporten logger du inn på vegne av virksomheten. Her kan du se og velge scopes.",
"settings_modal.maskinporten_tab_save_scopes_complete": "Lagret",
"settings_modal.maskinporten_tab_save_scopes_pending": "Lagrer...",
"settings_modal.maskinporten_tab_save_scopes_pending_spinner": "Lagrer scopes",
"settings_modal.maskinporten_tab_title": "Velg scopes fra Maskinporten",
"settings_modal.policy_tab_heading": "Tilganger",
"settings_modal.setup_tab_heading": "Oppsett",
Expand Down Expand Up @@ -1374,11 +1377,14 @@
"ux_editor.component_properties.stickyHeader": "Fest tittelraden",
"ux_editor.component_properties.style": "Stil",
"ux_editor.component_properties.subdomains": "Subdomener (kommaseparert)",
"ux_editor.component_properties.subform.choose_data_model": "Velg datamodell...",
"ux_editor.component_properties.subform.choose_layout_set": "Velg et underskjema...",
"ux_editor.component_properties.subform.choose_layout_set_description": "Velg først underskjemaet du vil bruke i Tabell for underskjema. Deretter kan du sette opp egenskapene for komponenten.",
"ux_editor.component_properties.subform.choose_layout_set_header": "Velg underskjemaet du vil bruke",
"ux_editor.component_properties.subform.choose_layout_set_label": "Velg et underskjema",
"ux_editor.component_properties.subform.create_layout_set_button": "Opprett et nytt underskjema",
"ux_editor.component_properties.subform.create_new_data_model": "Lag ny datamodell",
"ux_editor.component_properties.subform.create_new_data_model_label": "Navn på ny datamodell",
"ux_editor.component_properties.subform.created_layout_set_name": "Navn på underskjema",
"ux_editor.component_properties.subform.data_model_binding_label": "Velg datamodellknytning",
"ux_editor.component_properties.subform.data_model_empty_messsage": "Ingen tilgjengelige datamodeller",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ForwardedRef } from 'react';
import React from 'react';
import type { StudioTextResourceInputProps } from './StudioTextResourceInput';
import { StudioTextResourceInput } from './StudioTextResourceInput';
Expand All @@ -9,6 +10,9 @@ import { textResourcesMock } from '../../test-data/textResourcesMock';
import type { UserEvent } from '@testing-library/user-event';
import { userEvent } from '@testing-library/user-event';
import { getTextResourceById } from './utils';
import { testRefForwarding } from '../../test-utils/testRefForwarding';
import { testRootClassNameAppending } from '../../test-utils/testRootClassNameAppending';
import { testCustomAttributes } from '../../test-utils/testCustomAttributes';

// Test data:
const textResources: TextResource[] = textResourcesMock;
Expand Down Expand Up @@ -92,10 +96,28 @@ describe('StudioTextResourceInput', () => {
await switchToSearchMode(user);
expect(screen.getByText(currentId)).toBeInTheDocument();
});

it('Forwards the ref if given', () => {
testRefForwarding<HTMLInputElement>((ref) => renderTextResourceInput({}, ref), getValueField);
});

it('Appends the given class name to the root class', () => {
testRootClassNameAppending((className) => renderTextResourceInput({ className }));
});

it('Applies additional props to the input element', () => {
testCustomAttributes<HTMLInputElement, StudioTextResourceInputProps>(
renderTextResourceInput,
getValueField,
);
});
});

function renderTextResourceInput(props: Partial<StudioTextResourceInputProps> = {}): RenderResult {
return render(<StudioTextResourceInput {...defaultProps} {...props} />);
function renderTextResourceInput(
props: Partial<StudioTextResourceInputProps> = {},
ref?: ForwardedRef<HTMLInputElement>,
): RenderResult {
return render(<StudioTextResourceInput {...defaultProps} {...props} ref={ref} />);
}

function getValueField(): HTMLInputElement {
Expand Down
Loading

0 comments on commit a8cdd9a

Please sign in to comment.