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(ui-editor): enable component poc and feedback form #14210

Merged
merged 23 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const buttonTexts: ButtonTexts = {
};

const heading = 'Heading';
const description = 'Description';

describe('FeedbackForm', () => {
it('should render FeedbackForm', () => {
Expand Down Expand Up @@ -102,8 +103,10 @@ const renderFeedbackForm = ({
render(
<FeedbackFormContext.Provider value={{ answers: {}, setAnswers: setAnswers || jest.fn() }}>
<FeedbackForm
id='test'
buttonTexts={buttonTexts}
heading={heading}
description={description}
questions={questions}
position={position || 'inline'}
onSubmit={jest.fn()}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useRef } from 'react';
import { StudioButton, StudioModal } from '@studio/components';
import { StudioButton, StudioModal, StudioParagraph } from '@studio/components';
import type { ButtonTexts, QuestionConfig, QuestionsProps } from '../types/QuestionsProps';
import { YesNoQuestion } from './Question/YesNoQuestion';
import { useFeedbackFormContext } from '../contexts/FeedbackFormContext';
Expand All @@ -9,17 +9,21 @@ import { getDefaultAnswerValueForQuestion } from '../utils/questionUtils';
import type { AnswerType } from '../types/AnswerType';

type FeedbackFormProps = {
id: string;
buttonTexts: ButtonTexts;
heading: string;
description: string;
questions: QuestionConfig[];
position?: 'inline' | 'fixed';
onSubmit: (answers: Record<string, AnswerType>) => void;
};

export function FeedbackForm({
id,
questions,
buttonTexts,
heading,
description,
position = 'inline',
onSubmit,
}: FeedbackFormProps): React.ReactElement {
Expand All @@ -42,7 +46,10 @@ export function FeedbackForm({
};

const handleSubmit = () => {
onSubmit(answers);
onSubmit({
...answers,
feedbackFormId: id,
});
handleCloseModal();
};

Expand Down Expand Up @@ -77,6 +84,9 @@ export function FeedbackForm({
closeButtonTitle={buttonTexts.close}
ref={modalRef}
>
wrt95 marked this conversation as resolved.
Show resolved Hide resolved
<StudioParagraph size='sm' spacing={true}>
{description}
</StudioParagraph>
{questions.map((question) => {
return renderQuestion(question);
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import React, { type ChangeEvent } from 'react';
import type { QuestionsProps } from '../../types/QuestionsProps';
import { StudioTextfield } from '@studio/components';
import { StudioTextarea } from '@studio/components';
import { useDebounce } from '@studio/hooks';

export function TextQuestion({ id, label, value, onChange }: QuestionsProps): React.ReactElement {
const { debounce } = useDebounce({ debounceTimeInMs: 500 });
const debouncedOnChange = (newValue: string) => debounce(() => onChange(id, newValue));
return (
<StudioTextfield
<StudioTextarea
size='sm'
id={id}
label={label}
value={value}
onChange={(e: ChangeEvent<HTMLInputElement>) => debouncedOnChange(e.target.value)}
onChange={(e: ChangeEvent<HTMLTextAreaElement>) => debouncedOnChange(e.target.value)}
/>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { StudioButton, StudioParagraph } from '@studio/components';
import { StudioButton, StudioLabelAsParagraph } from '@studio/components';
import { ThumbDownFillIcon, ThumbDownIcon, ThumbUpFillIcon, ThumbUpIcon } from '@studio/icons';
import type { QuestionsProps } from '../../types/QuestionsProps';
import classes from './YesNoQuestion.module.css';
Expand All @@ -26,7 +26,7 @@ export function YesNoQuestion({ id, label, value, buttonLabels, onChange }: YesN

return (
<div>
<StudioParagraph>{label}</StudioParagraph>
<StudioLabelAsParagraph size='sm'>{label}</StudioLabelAsParagraph>
<div className={classes.buttons}>
<StudioButton
variant='tertiary'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ describe('FeedbackFormImpl', () => {
it('should render FeedbackFormImpl', () => {
const feedbackForm = new FeedbackFormImpl({
onSubmit: jest.fn(),
id: 'test',
buttonTexts: {
submit: 'Submit',
trigger: 'Give feedback',
close: 'Close',
},
heading: 'Give feedback - heading',
description: 'Description',
questions: mockQuestions,
});

Expand All @@ -25,13 +27,15 @@ describe('FeedbackFormImpl', () => {
it('should open form modal when trigger button is clicked', async () => {
const user = userEvent.setup();
const feedbackForm = new FeedbackFormImpl({
id: 'test',
onSubmit: jest.fn(),
buttonTexts: {
submit: 'Submit',
trigger: 'Give feedback',
close: 'Close',
},
heading: 'Give feedback - heading',
description: 'Description',
questions: mockQuestions,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,41 @@ import { FeedbackForm } from '../FeedbackForm/FeedbackForm';
import type { AnswerType } from '../types/AnswerType';

export class FeedbackFormImpl {
private readonly id: string;
private readonly buttonTexts: ButtonTexts;
private readonly heading: string;
private readonly description: string;
private readonly questions: QuestionConfig[];
private readonly position: 'inline' | 'fixed' = 'inline';
private readonly onSubmit: (answers: Record<string, any>) => void;

constructor(config: {
id: string;
buttonTexts: ButtonTexts;
heading: string;
description: string;
questions: QuestionConfig[];
position?: 'inline' | 'fixed';
onSubmit: (answers: Record<string, AnswerType>) => void;
}) {
this.id = config.id;
this.buttonTexts = config.buttonTexts;
this.heading = config.heading;
this.description = config.description;
this.questions = config.questions;
this.getFeedbackForm = this.getFeedbackForm.bind(this);
this.position = config.position || 'inline';
this.onSubmit = config.onSubmit;
}

public getFeedbackForm(): React.ReactNode {
public getFeedbackForm(): React.ReactElement {
return (
<FeedbackFormContextProvider>
<FeedbackForm
id={this.id}
buttonTexts={this.buttonTexts}
heading={this.heading}
description={this.description}
questions={this.questions}
position={this.position}
onSubmit={this.onSubmit}
Expand Down
3 changes: 3 additions & 0 deletions frontend/packages/shared/src/api/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export const dataModelAddXsdFromRepoPath = (org, app, filePath) => `${basePath}/
// Deployment
// See frontend/app-development/utils/urlHelper.ts Deployments

// Feedback form
export const submitFeedbackPath = (org, app) => `${basePath}/${org}/${app}/feedbackform/submit`; // Post

// FormEditor
export const ruleHandlerPath = (org, app, layoutSetName) => `${basePath}/${org}/${app}/app-development/rule-handler?${s({ layoutSetName })}`; // Get, Post
export const widgetSettingsPath = (org, app) => `${basePath}/${org}/${app}/app-development/widget-settings`; // Get
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.componentButtonInline {
height: 80px;
width: 140px;
margin: 8px;
height: 60px;
width: 120px;
margin: 4px;
padding: 6px;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
.componentButton {
margin: 4px;
width: 140px;
height: 100px;
width: 130px;
height: 80px;
padding: 2px;
overflow: wrap;
}

.componentButtonInline {
height: 80px;
width: 140px;
margin: 8px;
height: 60px;
width: 120px;
margin: 4px;
padding: 2px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function ComponentButton({
<StudioButton
variant={selected ? 'primary' : 'secondary'}
onClick={onClick}
size='sm'
size='xs'
aria-label={tooltipContent}
className={inline ? classes.componentButtonInline : classes.componentButton}
title={tooltipContent}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ReactElement } from 'react';

Check failure on line 1 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx

View workflow job for this annotation

GitHub Actions / Typechecking and linting

All imports in the declaration are only used as types. Use `import type`
nkylstad marked this conversation as resolved.
Show resolved Hide resolved
import { submitFeedbackPath } from 'app-shared/api/paths';
import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams';
import { post } from 'app-shared/utils/networking';
import { FeedbackFormImpl } from '@studio/feedback-form';
import { toast } from 'react-toastify';

export function FeedbackForm(): ReactElement {
const { org, app } = useStudioEnvironmentParams();

Check warning on line 9 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx#L8-L9

Added lines #L8 - L9 were not covered by tests

const submitFeedback = async (answers: Record<string, string>) => {

Check warning on line 11 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx#L11

Added line #L11 was not covered by tests
wrt95 marked this conversation as resolved.
Show resolved Hide resolved
// Using explicit texts here to avoid adding these potentially
// temporary and unnecessary translations to the translation files.
try {

Check warning on line 14 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx#L14

Added line #L14 was not covered by tests
// Using regular axios post rather than a mutation hook, since we are not storing
// the feedback in the cache, nor are we updating any state.
await post(submitFeedbackPath(org, app), { answers: { ...answers } });
toast.success('Takk for tilbakemeldingen!');

Check warning on line 18 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx#L17-L18

Added lines #L17 - L18 were not covered by tests
wrt95 marked this conversation as resolved.
Show resolved Hide resolved
nkylstad marked this conversation as resolved.
Show resolved Hide resolved
} catch (error) {
console.error('Failed to submit feedback', error);
toast.error('Noe gikk galt. Prøv igjen senere.');

Check warning on line 21 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx#L20-L21

Added lines #L20 - L21 were not covered by tests
}
};

// Using explicit texts here to avoid adding these potentially
// temporary and unnecessary translations to the translation files.
const feedbackForm = new FeedbackFormImpl({

Check warning on line 27 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx#L27

Added line #L27 was not covered by tests
id: 'add-component-poc-feedback',
onSubmit: submitFeedback,
buttonTexts: {
submit: 'Send',
trigger: 'Gi tilbakemelding',
close: 'Lukk',
},
heading: 'Gi tilbakemelding',
description:
'Hei! Vi ser du tester et nytt design for å legge til komponenter og vil gjerne høre hva du synes!',
nkylstad marked this conversation as resolved.
Show resolved Hide resolved
position: 'fixed',
questions: [
{
id: 'bedreJaNei',
type: 'yesNo',
questionText: 'Likte du dette designet bedre?',
buttonLabels: {
yes: 'Ja',
no: 'Nei',
},
},
{
id: 'kommentar',
type: 'text',
questionText: 'Har du kommentarer eller forslag til forbedringer?',
},
],
});

return feedbackForm.getFeedbackForm();

Check warning on line 57 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/containers/DesignView/AddItem/FeedbackForm.tsx#L57

Added line #L57 was not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.switchWrapper {
display: flex;
flex-direction: row;
gap: var(--fds-spacing-2);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import { FeedbackForm } from './FeedbackForm';
import { StudioParagraph, StudioSwitch } from '@studio/components';
import {
addFeatureFlagToLocalStorage,
removeFeatureFlagFromLocalStorage,
shouldDisplayFeature,
} from 'app-shared/utils/featureToggleUtils';
import { HelpText } from '@digdir/designsystemet-react';
import classes from './ToggleAddComponentPoc.module.css';

/**
* Component that toggles the AddComponentModal POC and
* displays the feedback form if the feature flag is enabled
* NOTE: Since this is a poc, and the switch at some point will be
* removed, all texts are explicit in this file, rather than being
* fetched from the translation files.
* @returns The ToggleAddComponentPoc component
*/
export function ToggleAddComponentPoc(): React.ReactElement {
const toggleComponentModalPocAndReload = () => {
if (shouldDisplayFeature('addComponentModal')) {
removeFeatureFlagFromLocalStorage('addComponentModal');

Check warning on line 23 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/ToggleAddComponentPoc.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/containers/DesignView/AddItem/ToggleAddComponentPoc.tsx#L23

Added line #L23 was not covered by tests
} else {
addFeatureFlagToLocalStorage('addComponentModal');

Check warning on line 25 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/ToggleAddComponentPoc.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/containers/DesignView/AddItem/ToggleAddComponentPoc.tsx#L25

Added line #L25 was not covered by tests
}
window.location.reload();

Check warning on line 27 in frontend/packages/ux-editor/src/containers/DesignView/AddItem/ToggleAddComponentPoc.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/packages/ux-editor/src/containers/DesignView/AddItem/ToggleAddComponentPoc.tsx#L27

Added line #L27 was not covered by tests
};
return (
<>
<div className={classes.switchWrapper}>
<StudioSwitch
checked={shouldDisplayFeature('addComponentModal')}
onChange={toggleComponentModalPocAndReload}
size='sm'
>
Prøv nytt design
</StudioSwitch>
<HelpText
size='sm'
title='Prøv vårt nye design for å legge til komponenter'
placement='bottom-start'
>
<StudioParagraph spacing size='sm'>
Vi jobber med brukeropplevelsen i Studio. Vil du prøve vårt forslag til nytt design for
å legge til komponenter?
</StudioParagraph>
<StudioParagraph spacing size='sm'>
Du kan fortelle oss hva du synes om det nye designet ved å trykke på &quot;Gi
tilbakemelding&quot; nederst til høyre på siden.
</StudioParagraph>
</HelpText>
</div>
{shouldDisplayFeature('addComponentModal') && <FeedbackForm />}
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@ import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmen
import classes from './FormDesignerToolbar.module.css';
import { useLayoutSetsQuery } from 'app-shared/hooks/queries/useLayoutSetsQuery';
import { LayoutSetsContainer } from '../components/Elements/LayoutSetsContainer';
import { ToggleAddComponentPoc } from './DesignView/AddItem/ToggleAddComponentPoc';

export const FormDesignerToolbar = () => {
const { org, app } = useStudioEnvironmentParams();
const layoutSetsQuery = useLayoutSetsQuery(org, app);
const layoutSetNames = layoutSetsQuery?.data?.sets;

return <section className={classes.toolbar}>{layoutSetNames && <LayoutSetsContainer />}</section>;
return (
<section className={classes.toolbar}>
{layoutSetNames && <LayoutSetsContainer />}
{/* POC of new design for adding components*/}
<ToggleAddComponentPoc />
</section>
);
};
2 changes: 1 addition & 1 deletion frontend/packages/ux-editor/src/data/formItemConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,14 +578,14 @@ export const defaultComponents: ComponentType[] = [

export const allComponents: KeyValuePairs<ComponentType[]> = {
form: [ComponentType.Input, ComponentType.TextArea, ComponentType.Datepicker],
text: [ComponentType.Header, ComponentType.Paragraph, ComponentType.Panel, ComponentType.Alert],
select: [
ComponentType.Checkboxes,
ComponentType.RadioButtons,
ComponentType.Dropdown,
ComponentType.MultipleSelect,
ComponentType.Likert,
],
text: [ComponentType.Header, ComponentType.Paragraph, ComponentType.Panel, ComponentType.Alert],
info: [
ComponentType.InstanceInformation,
ComponentType.Image,
Expand Down
Loading