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

Feature/select single questions #24

Merged
merged 13 commits into from
Aug 23, 2023
120 changes: 120 additions & 0 deletions src/components/ChoiceCollectionSelectInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { useMemo, useState } from 'react';
import { isNotDefined } from '@togglecorp/fujs';
import { gql, useQuery } from '@apollo/client';
import { SearchSelectInput } from '@the-deep/deep-ui';

import {
ChoiceCollectionsQuery,
ChoiceCollectionsQueryVariables,
} from '#generated/types';

const CHOICE_COLLECTIONS = gql`
query ChoiceCollections(
$projectId: ID!,
$questionnaireId: ID!,
$search:String
) {
private {
projectScope(pk: $projectId) {
id
choiceCollections(
filters: {
questionnaire: {pk: $questionnaireId},
label: {iContains: $search }
}
) {
count
items {
id
label
name
questionnaireId
}
}
}
}
}
`;

type ChoiceCollection = NonNullable<ChoiceCollectionsQuery['private']['projectScope']>['choiceCollections']['items'][number];

const choiceCollectionKeySelector = (d: ChoiceCollection) => d.id;
const choiceCollectionLabelSelector = (d: ChoiceCollection) => d.label;

interface Props<T> {
projectId: string;
questionnaireId: string | null;
name: T;
label: string;
onChange: (value: string | undefined, name: T) => void;
value: string | null | undefined;
error: string | undefined;
}

function ChoiceCollectionSelectInput<T extends string>(props: Props<T>) {
const {
projectId,
questionnaireId,
name,
value,
label,
onChange,
error,
} = props;

const [
choiceCollectionOptions,
setChoiceCollectionOptions,
] = useState<ChoiceCollection[] | undefined | null>();

const [search, setSearch] = useState<string>();
const [opened, setOpened] = useState(false);

const optionsVariables = useMemo(() => {
if (isNotDefined(projectId) || isNotDefined(questionnaireId)
) {
return undefined;
}
return ({
projectId,
questionnaireId,
search,
});
}, [
projectId,
questionnaireId,
search,
]);

const {
data: choiceCollectionsResponse,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's fetch loading as well. We need to disable the SelectInput when the query is loading.

loading: choiceCollectionLoading,
} = useQuery<
ChoiceCollectionsQuery,
ChoiceCollectionsQueryVariables
>(CHOICE_COLLECTIONS, {
skip: isNotDefined(optionsVariables) || !opened,
variables: optionsVariables,
});
const searchOption = choiceCollectionsResponse?.private.projectScope?.choiceCollections.items;

return (
<SearchSelectInput
name={name}
error={error}
disabled={choiceCollectionLoading}
keySelector={choiceCollectionKeySelector}
labelSelector={choiceCollectionLabelSelector}
onChange={onChange}
onSearchValueChange={setSearch}
onOptionsChange={setChoiceCollectionOptions}
label={label}
searchOptions={searchOption}
options={choiceCollectionOptions}
onShowDropdownChange={setOpened}
value={value}
/>
);
}

export default ChoiceCollectionSelectInput;
94 changes: 94 additions & 0 deletions src/components/PillarSelectInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { useMemo } from 'react';
import { gql, useQuery } from '@apollo/client';
import { SelectInput } from '@the-deep/deep-ui';
import { isNotDefined } from '@togglecorp/fujs';

import { PillarsQuery, PillarsQueryVariables } from '#generated/types';

const PILLARS = gql`
query Pillars (
$projectId: ID!,
$questionnaireId: ID!,
) {
private {
projectScope(pk: $projectId) {
groups (filters: {questionnaire: {pk: $questionnaireId}}){
items {
id
name
label
parentId
questionnaireId
}
}
}
}
}
`;

type Pillar = NonNullable<PillarsQuery['private']['projectScope']>['groups']['items'][number];

const pillarKeySelector = (data: Pillar) => data.id;
const pillarLabelSelector = (data: Pillar) => data.label;

interface PillarProps<T>{
projectId: string;
name: T;
questionnaireId: string | null;
value: string | null | undefined;
error: string | undefined;
onChange: (value: string | undefined, name: T) => void;
}

function PillarSelectInput<T extends string>(props: PillarProps<T>) {
const {
projectId,
questionnaireId,
value,
error,
onChange,
name,
} = props;

const pillarsVariables = useMemo(() => {
if (isNotDefined(projectId) || isNotDefined(questionnaireId)) {
return undefined;
}
return ({
projectId,
questionnaireId,
});
}, [
projectId,
questionnaireId,
]);

const {
data: pillarsResponse,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's fetch loading as well.

loading: pillarsLoading,
} = useQuery<PillarsQuery, PillarsQueryVariables>(
PILLARS,
{
skip: isNotDefined(pillarsVariables),
variables: pillarsVariables,
},
);

const pillarsOptions = pillarsResponse?.private?.projectScope?.groups.items ?? [];

return (
<SelectInput
name={name}
label="Pillar and Sub pillar"
value={value}
error={error}
onChange={onChange}
keySelector={pillarKeySelector}
labelSelector={pillarLabelSelector}
options={pillarsOptions}
disabled={pillarsLoading}
/>
);
}

export default PillarSelectInput;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.preview {
display: flex;
flex-direction: column;
padding: var(--dui-spacing-extra-large) var(--dui-spacing-large);
gap: var(--dui-spacing-medium);

.checkbox-list {
display: flex;
flex-direction: column;
gap: var(--dui-spacing-small);
width: 10rem;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { useCallback, useMemo } from 'react';
import { gql, useQuery } from '@apollo/client';
import {
_cs,
isNotDefined,
noOp,
} from '@togglecorp/fujs';
import {
Checkbox,
ListView,
TextOutput,
} from '@the-deep/deep-ui';

import { MultipleOptionListQuery, MultipleOptionListQueryVariables } from '#generated/types';

import styles from './index.module.css';

const MULTIPLE_OPTION_LIST = gql`
query MultipleOptionList(
$projectId: ID!,
$choiceCollectionId: ID!,
) {
private {
projectScope(pk: $projectId) {
choiceCollection(pk: $choiceCollectionId) {
id
label
name
choices {
id
label
name
}
}
}
}
}
`;

type CheckboxType = NonNullable<NonNullable<MultipleOptionListQuery['private']['projectScope']>['choiceCollection']>['choices'][number];
const checkboxKeySelector = (d: CheckboxType) => d.id;

interface Props {
className?: string;
label?: string;
hint?: string | null;
choiceCollectionId: string | undefined | null;
projectId: string;
}

function SelectMultipleQuestionPreview(props: Props) {
const {
className,
label,
hint,
choiceCollectionId,
projectId,
} = props;

const optionListVariables = useMemo(() => {
if (isNotDefined(projectId) || isNotDefined(choiceCollectionId)) {
return undefined;
}
return ({
projectId,
choiceCollectionId,
});
}, [
projectId,
choiceCollectionId,
]);

const {
data: optionsListResponse,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's fetch loading.

loading: OptionsListLoading,
} = useQuery<MultipleOptionListQuery, MultipleOptionListQueryVariables>(
MULTIPLE_OPTION_LIST,
{
skip: isNotDefined(optionListVariables),
variables: optionListVariables,
},
);

const checkboxListRendererParams = useCallback((_: string, datum: CheckboxType) => ({
label: datum?.label,
name: 'choiceCollection',
value: false,
readOnly: true,
onChange: noOp,
}), []);

return (
<div className={_cs(styles.preview, className)}>
<TextOutput
value={label ?? 'Title'}
description={hint ?? 'Choose One'}
spacing="none"
block
/>
<ListView
className={styles.checkboxList}
data={optionsListResponse?.private?.projectScope?.choiceCollection?.choices}
keySelector={checkboxKeySelector}
renderer={Checkbox}
rendererParams={checkboxListRendererParams}
filtered={false}
errored={false}
pending={OptionsListLoading}
/>
</div>
);
}

export default SelectMultipleQuestionPreview;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.preview {
display: flex;
flex-direction: column;
padding: var(--dui-spacing-extra-large) var(--dui-spacing-large);
gap: var(--dui-spacing-medium);

.question-list {
display: flex;
align-items: flex-start;
flex-direction: column;
gap: var(--dui-spacing-small);
width: 10rem;
}
}
Loading
Loading