Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

"All items are selected" displayed when all items aren't selected #64

Open
jmeyers91 opened this issue Jan 9, 2020 · 2 comments
Open

Comments

@jmeyers91
Copy link

I've got a form with several multi-select components on it and the options for some of them are dependent on which options are selected in others. The problem I'm running into is when I check and uncheck options in the select that is depended on, the other select seems to end up in a state where some but not all options are selected, but it shows all options as selected.

Here's a screenshot of what I mean:
Screen Shot 2020-01-09 at 12 44 00 PM

And the code for the form:

import React, { FC, useState, useMemo, useCallback } from "react";
import styled from "styled-components/macro";
import MultiSelect from "@khanacademy/react-multi-select";
import { IoIosCalendar } from "react-icons/io";
import { FiDownload } from "react-icons/fi";
import Form from "./Form";
import Row from "./Row";
import useAsyncEffect from "../utils/useAsyncEffect";
import getTeachers from "../api/teachers/getTeachers";
import Teacher from "shared/lib/types/Teacher";
import capitalizeFirst from "shared/lib/utils/capitalizeFirst";
import getAssignmentCategories from "../api/assignments/getAssignmentCategories";
import AssignmentCategory from "shared/lib/types/AssignmentCategory";
import getAllUnits from "../api/units/getAllUnits";
import Unit from "shared/lib/types/Unit";
import Assignment from "shared/lib/types/Assignment";
import getAllAssignments from "../api/assignments/getAllAssignments";
import LabeledSwitch from "./LabeledSwitch";
import DateInput from "./DateInput";
import Column from "./Column";
import BlockButton from "./BlockButton";
import ResponseCsvOptions from "shared/lib/types/ResponseCsvOptions";

interface Props {
  onSubmit(value: Value): any;
}

interface Data {
  teachers: Teacher[];
  categories: AssignmentCategory[];
  units: Unit[];
  assignments: Assignment[];
}

export type Value = ResponseCsvOptions;

const CsvExportForm: FC<Props> = props => {
  const { onSubmit, ...rest } = props;
  const [data, setData] = useState<Data | null>(null);
  const [value, setValue] = useState<Value>({
    startDate: null,
    endDate: null,
    teacherIds: [],
    unitIds: [],
    assignmentIds: [],
    categoryIds: [],
    includeTeacherUnits: false
  });

  useAsyncEffect(async () => {
    const teachers = await getTeachers();
    const categories = await getAssignmentCategories();
    const units = await getAllUnits();
    const assignments = await getAllAssignments();

    setData({
      teachers,
      categories,
      units,
      assignments
    });
  }, []);

  const teacherOptions = useMemo(() => {
    if (!data) {
      return [];
    }
    return data.teachers.map(teacher => ({
      value: teacher.id,
      label: `${capitalizeFirst(teacher.firstName)} ${capitalizeFirst(
        teacher.lastName
      )}`
    }));
  }, [data]);

  const unitOptions = useMemo(() => {
    if (!data) {
      return [];
    }
    return data.units
      .filter(unit => {
        // Skip units that belong to unselected teachers
        if (unit.teacherId && !value.teacherIds.includes(unit.teacherId)) {
          return false;
        }

        // Skip units that belong to unselected categories
        if (unit.categoryId) {
          return value.categoryIds.includes(unit.categoryId);
        } else {
          // Only show teacher units if the teacher authored switch is on
          return value.includeTeacherUnits;
        }
      })
      .map(unit => ({
        value: unit.id,
        label: unit.name
      }));
  }, [data, value]);

  const assignmentOptions = useMemo(() => {
    if (!data) {
      return [];
    }
    return data.assignments
      .filter(assignment => {
        return value.unitIds.includes(assignment.unitId);
      })
      .map(assignment => ({
        value: assignment.id,
        label: `${assignment.title} (${assignment.subTitle})`
      }));
  }, [data, value]);

  const handleTeachersChange = useCallback(
    (teacherIds: number[]) => {
      setValue(value => {
        if (!data) {
          return value;
        }
        const newUnitIds = value.unitIds.filter(unitId => {
          const unit = data.units.find(unit => unit.id === unitId);
          if (!unit) {
            return false;
          }
          if (unit.teacherId) {
            return (
              value.includeTeacherUnits && teacherIds.includes(unit.teacherId)
            );
          }
          return true;
        });

        return { ...value, teacherIds, unitIds: newUnitIds };
      });
    },
    [data]
  );

  const handleUnitsChange = useCallback((unitIds: number[]) => {
    setValue(value => ({ ...value, unitIds }));
  }, []);

  const handleAssignmentsChange = useCallback((assignmentIds: number[]) => {
    setValue(value => ({ ...value, assignmentIds }));
  }, []);

  const handleCategoriesChange = useCallback((categoryId: number) => {
    setValue(value => {
      const { categoryIds } = value;
      let newCategoryIds;

      if (categoryIds.includes(categoryId)) {
        newCategoryIds = categoryIds.filter(id => id !== categoryId);
      } else {
        newCategoryIds = [...categoryIds, categoryId];
      }

      return {
        ...value,
        categoryIds: newCategoryIds
      };
    });
  }, []);

  const handleTeacherUnitsChange = useCallback((teacherUnits: boolean) => {
    setValue(value => ({ ...value, includeTeacherUnits: teacherUnits }));
  }, []);

  const handleSubmit = useCallback(() => {
    onSubmit(value);
  }, [onSubmit, value]);

  if (!data) {
    return null;
  }

  return (
    <Form {...rest} onSubmit={handleSubmit}>
      <SectionLabel>Date Range</SectionLabel>
      <Section>
        <DateInputColumn>
          <DateInputLabel>Start</DateInputLabel>
          <StyledDateInput placeholder="mm/dd/yyyy" />
          <IoIosCalendar size={28} />
        </DateInputColumn>

        <DateInputColumn>
          <DateInputLabel>End</DateInputLabel>
          <StyledDateInput placeholder="mm/dd/yyyy" />
          <IoIosCalendar size={28} />
        </DateInputColumn>
      </Section>

      <SectionLabel>Products</SectionLabel>
      <Section>
        {data.categories.map(category => (
          <LabeledSwitch
            key={category.id}
            checked={value.categoryIds.includes(category.id)}
            onChange={() => handleCategoriesChange(category.id)}
          >
            {category.name}
          </LabeledSwitch>
        ))}
        <LabeledSwitch
          checked={value.includeTeacherUnits}
          onChange={handleTeacherUnitsChange}
        >
          TEACHER AUTHORED
        </LabeledSwitch>
      </Section>

      <SectionLabel>Units</SectionLabel>
      <Section>
        <MultiSelect
          selected={value.unitIds}
          options={unitOptions}
          onSelectedChanged={handleUnitsChange}
        />
      </Section>

      <SectionLabel>Assignments</SectionLabel>
      <Section>
        <MultiSelect
          selected={value.assignmentIds}
          options={assignmentOptions}
          onSelectedChanged={handleAssignmentsChange}
        />
      </Section>

      <SectionLabel>Teachers</SectionLabel>
      <Section>
        <MultiSelect
          selected={value.teacherIds}
          options={teacherOptions}
          onSelectedChanged={handleTeachersChange}
        />
      </Section>

      <DownloadButton>
        <FiDownload size={20} />
        Download CSV
      </DownloadButton>
    </Form>
  );
};

export default styled(CsvExportForm)`
  .multi-select {
    width: 459px;
  }

  .dropdown-heading {
    border-radius: 8px !important;
  }

  .dropdown-heading-dropdown-arrow {
    background-color: #979797;
    width: 35px !important;
    padding-right: 0 !important;
  }

  .dropdown-heading-dropdown-arrow > span {
    border-color: white transparent transparent !important;
  }

  .dropdown[aria-expanded="true"] .dropdown-heading-dropdown-arrow > span {
    border-color: transparent transparent white !important;
  }
`;

const SectionLabel = styled("h3")`
  color: #000000;
  font-family: Lato;
  font-size: 21px;
  line-height: 25px;
  margin-bottom: 20px;
  font-weight: normal;
`;

const Section = styled(Row)`
  padding-left: 23px;
  margin-bottom: 32px;

  ${LabeledSwitch} + ${LabeledSwitch} {
    margin-left: 40px;
  }
`;

const StyledDateInput = styled(DateInput)`
  input {
    height: 37px;
    width: 219px;
    border: 1px solid #979797;
    border-radius: 8px;
    background-color: #ffffff;
    padding-left: 8px;
  }

  input::placeholder {
    color: #000000;
    font-size: 14px;
    line-height: 17px;
  }
`;

const DateInputColumn = styled(Column)`
  position: relative;

  & + & {
    margin-left: 21px;
  }

  svg {
    pointer-events: none;
    position: absolute;
    right: 13px;
    top: 24px;
  }
`;

const DateInputLabel = styled("label")`
  color: #7e7e7e;
  font-size: 14px;
  font-weight: 900;
  margin-bottom: 3px;
  margin-left: 8px;
`;

const DownloadButton = styled(BlockButton)`
  width: 160px;
  background-color: #000000;
  color: #fff;
  font-size: 14px;
  font-weight: 500;
  margin-left: 23px;

  svg {
    margin-right: 8px;
  }
`;
@ambreine31
Copy link

I have the same problem tell me if you found a solution !

@sahil-isima
Copy link

I also got into similar issue for couple of minutes. But later figured out that i am only sending wrong value of selected array. Make sure you are sending values with false etc in it. It select Select All option based on following:
length of option === length of selected item

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants