All files / src/shared/GiveExportModal/components ExportModalContent.tsx

100% Statements 43/43
89.28% Branches 25/28
100% Functions 15/15
100% Lines 41/41

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157                            90x   90x     376x 41x                             335x   335x                                                                                           26x 384x   87x     99x   36x   161x   1x         36x 36x 36x   36x 1x 2x 1x 3x   1x 1x         1x         1x     36x 1x 1x 2x   1x 2x   1x     36x 15x 155x   153x     36x 2x 1x   1x       36x            
import { ExportField, RadioMultiselect } from "../giveExportTypes";
import { Stack } from "@mui/material";
import GiveText from "@shared/Text/GiveText";
import { useAppTheme } from "@theme/v2/Provider";
import ExportRadioComponent from "./ExportRadioComponent";
import ExportSelectComponent from "./ExportSelectComponent";
import { HFGiveInput } from "@shared/HFInputs/HFGiveInput/HFGiveInput";
import GiveButton from "@shared/Button/GiveButton";
import ExportRadioMultiselect from "./ExportRadioMultiselect";
import GiveLink from "@shared/Link/GiveLink";
import { useFormContext } from "react-hook-form";
import { useMemo } from "react";
 
function ExportModalContent({ fieldRawData }: { fieldRawData: ExportField[] }) {
  const theme = useAppTheme();
 
  return (
    <Stack gap="37px" px="20px" py="37px">
      {fieldRawData?.map((field) => {
        if (field.fieldType === "button") {
          return (
            <GiveButton
              key={field.label}
              label={field.label}
              onClick={field.onClick}
              size="large"
              variant="filled"
              color="light"
              fullWidth
              sx={{ marginTop: "-17px" }}
              //between other sections the gap should 37px, but to follow the right logic for the export view,
              //the button should be treated its 'own' section, but here the gap should be smaller
            />
          );
        }
        const { secondaryField, label, fieldType } = field;
 
        return (
          <Stack key={field.label}>
            <Stack
              direction="row"
              alignItems="center"
              justifyContent="space-between"
            >
              <GiveText fontWeight={400} fontSize="16px">
                {label}
              </GiveText>
              {fieldType === "radio-multiselect" && (
                <MultiSelectButton field={field} />
              )}
            </Stack>
            <Stack
              width="100%"
              p="20px"
              mt="20px"
              borderRadius="20px"
              bgcolor={theme.palette.surface?.primary}
              gap="20px"
            >
              {field.controlLabel ? (
                <Stack gap="8px">
                  <GiveText variant="bodyS">{field.controlLabel}</GiveText>
                  <ComponentSwitcher field={field} />
                </Stack>
              ) : (
                <ComponentSwitcher field={field} />
              )}
              {secondaryField && (
                <Stack gap="8px">
                  <GiveText variant="bodyS">{secondaryField.label}</GiveText>
                  <ComponentSwitcher field={secondaryField} />
                </Stack>
              )}
            </Stack>
          </Stack>
        );
      })}
    </Stack>
  );
}
 
export default ExportModalContent;
 
const ComponentSwitcher = ({ field }: { field: ExportField }) => {
  switch (field.fieldType) {
    case "input":
      return <HFGiveInput name={field.fieldName} />;
 
    case "radio":
      return <ExportRadioComponent field={field} />;
    case "radio-multiselect":
      return <ExportRadioMultiselect field={field} />;
    case "select":
      return <ExportSelectComponent field={field} />;
    default:
      return null;
  }
};
 
function MultiSelectButton({ field }: { field: RadioMultiselect }) {
  const { setValue, watch } = useFormContext();
  const { fieldName, options } = field;
  const selectedValues: string[] = watch(fieldName) || [];
 
  const deselectSpecificSection = () => {
    const currentSelectedValues = watch(fieldName) || [];
    const optionsValues = options.map((option) => option.value);
    const deselectedValues = currentSelectedValues.filter(
      (value: string) => !optionsValues.includes(value),
    );
    const hadSelectionInSection = optionsValues.some((opt) =>
      currentSelectedValues.includes(opt),
    );
    // Only keep one option in this section when deselecting would leave the entire selection empty
    // (no other section has any selection). Otherwise just apply the deselect.
    const valueToSet =
      hadSelectionInSection &&
      options.length > 0 &&
      deselectedValues.length === 0
        ? [options[options.length - 1].value]
        : deselectedValues;
    setValue(fieldName, valueToSet, { shouldValidate: true });
  };
 
  const selectAllSpecificSection = () => {
    const currentSelectedValues: string[] = watch(fieldName) || [];
    const optionsValues: string[] = options.map(
      (option) => option.value as string,
    );
    const unselectedOptions = optionsValues.filter(
      (value: string) => !currentSelectedValues.includes(value),
    );
    setValue(fieldName, [...currentSelectedValues, ...unselectedOptions]);
  };
 
  const allSelected = useMemo(() => {
    const optionsValues: string[] = options.map(
      (option) => option.value as string,
    );
    return optionsValues.every((value) => selectedValues.includes(value));
  }, [selectedValues, options]);
 
  const handleMultiSelect = () => {
    if (allSelected) {
      deselectSpecificSection();
    } else {
      selectAllSpecificSection();
    }
  };
 
  return (
    <GiveLink component="button" color="secondary" onClick={handleMultiSelect}>
      {allSelected ? "Deselect All" : "Select All"}
    </GiveLink>
  );
}