osuresearch / ui

Ohio State Research UI
https://osuresearch.github.io/ui/main
MIT License
6 stars 3 forks source link

Add toggle all feature to `CheckboxSetField` #73

Open McManning opened 1 year ago

McManning commented 1 year ago

I find myself having to manually implement a toggle all checkbox as part of a set. This would be a good candidate for a feature option.

Here's my current implementation for a component that selects from a list of groups:

import { useGroupList } from "@/hooks/useGroupList"
import { CheckboxField, CheckboxSetField, Item } from "@osuresearch/ui";
import React, { useState } from "react";

export type GroupSelectFieldProps = {
  value?: string[]
  onChange: (ids: string[] | undefined) => void

  selectAllLabel: React.ReactNode
}

/**
 * Allows the user to select one or more groups from
 * the list of all groups they have access to.
 */
export function GroupSelectField({ value, selectAllLabel, onChange }: GroupSelectFieldProps) {
  const { groups, loading, error } = useGroupList();

  if (!groups) {
    return null;
  }

  const handleChange = (ids?: string[]) => {
    onChange(ids);
  }

  const hasSelection = value && value.length > 0;
  const hasPartialSelection = hasSelection && value.length !== groups.length;

  const toggleAll = (on?: boolean) => {
    if (hasSelection && value.length === groups.length) {
      onChange(undefined);
    }
    else {
      onChange(groups.map((g) => g.id));
    }
  }

  return (
    <div>
      <CheckboxField
        name="all-groups"
        value={value ? value.length > 0 : false}
        isIndeterminate={hasPartialSelection}
        onChange={toggleAll}
        label={selectAllLabel}
      />
      <CheckboxSetField pt="sm" pl="xl" name="groups" value={value ?? []} onChange={handleChange} aria-label="Select one or more groups">
        {groups.map((group) =>
          <Item key={group.id}>{group.id}</Item>
        )}
      </CheckboxSetField>
    </div>
  )
}

Visually it looks something like the following:

image