Skip to content

Conversation

britt6612
Copy link
Collaborator

@britt6612 britt6612 commented Mar 22, 2024

What does this PR do?

This is a proposal for a ToggleGroup component to follow on which design work that has been done.

The ToggleGroup is a Box with Buttons within it. The default type is a user can select one option from the group as far as if a user wants more options they pass multiple similar to how Select uses the multiple prop. This component provides the layout of the container which has rounded edges, direction='row', and a right border that is served as a separator since gap='none.

// API
  multiple?: boolean;
  onToggle?: (event: React.ClickEvent + value) => void;
  options?: (
    | string
    | {
        icon?: React.ReactNode | JSX.Element;
        label?: string | React.ReactNode;
        value: string;
      }
  )[];
  value?: string | string[];
base.js
{
  toggleGroup?: {
    button?: {
      pad?: PadType;
      iconOnly?: {
        pad?: PadType;
      };
    };
    container?: BoxProps;
    divider?: {
      color?: ColorType;
    };
  };
};

Rationale for certain naming/behavior decisions

What: ToggleGroup is a specialized group of buttons that supports single and multiple selection/toggling.

Theming approach: Because ToggleGroup is built with buttons, inherit a theme’s button styling as is and minimize the custom overrides that ToggleGroup applies to the buttons. For any overrides, honor the “button” theme mentalities (API, any logic/conditional styling/etc.).

Necessary overrides:

  • To match rounding of ToggleGroup container, override border-radius of individual buttons.
  • Padding behavior:
    • For theme.button.default (“kind”) themes: the approach is generally for border width to be subtracted from the padding (so people might expect this as they built up their theme styling). In how this component is built, the border is on the container Box, so we should do the subtraction for them.
    • In non “kind” themes, the caller is always responsible for “pad minus border” calculation themselves, so we shouldn’t do any calculation.

Stylistic overrides:

  • Base theme has a “brand” color border that comes through for buttons with labels → Remove that so the container border and divider border is the style
  • Base theme has different hover behaviors for icon-only (no hover background) and buttons with labels (box-shadow that increases border width) → Since the ToggleGroup buttons don’t have any individual border to begin width, don’t add the “purple” border. The “purple” border would be inconsistent with other controls because we don’t typically add a border onto a component if its default state didn’t have one.
  • Should base theme be inheriting the “button” border color for the container?
    • No, will be more prominent than other controls, keep the subtle container border. Caller can always override in their theme if they want otherwise.

What determined the base.js toggleGroup.button.pad?

Icon-only cases are the main use case for the ToggleGroup. In grommet theme, icon-only and labelled buttons have different dimensions. In order to lean towards the 80% case for ToggleGroup use cases and maintain consistency across different combinations of ToggleGroup options, aligning the dimensions for all ToggleGroups to that of base.js icon-only button (48px tall).

Why named “ToggleGroup”?

When viewing Google search trends it was found that toggleGroup was used more across the wider region.
Another downside to using ToggleButtonGroup is how long the word is and in many places in the code base we would need to disable eslint for the imports since the line would be over a amount of characters.
Did button actually have any meaning even though this component was made with a Button this did not necessarily need to be made known to the user.

Why enforce a value once one is selected in single selection?

80% of use cases all have one selected option for a toggleGroup. For our specific need for this component a use case was toggling through either a map list or data view where at least one of those needed to be selected.

Why onToggle?

This handler is called any time a button is clicked, effectively "toggling" an individual button. Initially we had onChange, but this felt in accurate and misaligned with the general Grommet mentality to honor native HTML events. In this case, it's not a change event but just a click event.

Where should the reviewer start?

ToggleGroup.js

What testing has been done on this PR?

storybook. Additional test code for HPE vs Grommet theming:

import React, { useState } from 'react';
import {
  Box,
  Button,
  ToggleGroup,
  Text,
  Grommet,
  Toolbar,
  DataFilters,
  Data,
  DataSearch,
  DataSummary,
} from 'grommet';
import { deepMerge } from 'grommet-icons/utils';
import { hpe } from 'grommet-theme-hpe';
import {
  Bold,
  Italic,
  List,
  MapLocation,
  Table,
  Underline,
} from 'grommet-icons';

const theme = deepMerge(hpe, {
  toggleGroup: {
    button: {
      pad: {
        vertical: 'xsmall',
        horizontal: 'small',
      },
      iconOnly: {
        pad: '9px',
      },
    },
  },
});

const Content = () => {
  const [value, setValue] = useState(['Option 2']);

  return (
    <Box direction="row" align="start" gap="small">
      <ToggleGroup
        options={['Option 1', 'Option 2', 'Option 3']}
        value={value}
        onToggle={(e) => {
          if (e.value.length) setValue(e.value);
        }}
        multiple
      />
      <ToggleGroup
        options={[
          { icon: <Bold />, value: 'b' },
          { icon: <Italic />, label: 'Italic', value: 'i' },
          { icon: <Underline />, value: 'u' },
        ]}
        multiple
      />
      <Button label="Button" primary />
      <Button icon={<Bold />} primary />
    </Box>
  );
};

const Views = () => (
  <ToggleGroup
    options={[
      { icon: <MapLocation />, value: 'b' },
      { icon: <List />, value: 'i' },
      { icon: <Table />, value: 'u' },
    ]}
    defaultValue="b"
  />
);

const ToolbarView = () => (
  <Box>
    <Data data={[]}>
      <Toolbar>
        <DataSearch />
        <DataFilters layer />
        <Views />
        <Box flex />
        <Button label="Actions" />
      </Toolbar>
      <DataSummary />
    </Data>
  </Box>
);

export const Controlled = () => (
  <Box gap="large" overflow="auto">
    <Box gap="large" pad="large">
      <Text>In multiple selection mode, enforce at least one selection.</Text>
      <Content />
      <Grommet theme={theme}>
        <Content />
      </Grommet>
    </Box>
    <Box gap="large" pad="large">
      {/* <Text>In multiple selection mode, enforce at least one selection.</Text> */}
      <ToolbarView />
      <Grommet theme={theme}>
        <ToolbarView />
      </Grommet>
    </Box>
  </Box>
);

export default {
  title: 'Controls/ToggleGroup/Controlled',
};


How should this be manually tested?

storybook

Do Jest tests follow these best practices?

  • - Need to add tests
  • screen is used for querying.
  • The correct query is used. (Refer to this list of queries)
  • asFragment() is used for snapshot testing.

Any background context you want to provide?

NEW ToggleGroup component was needed

What are the relevant issues?

closes #grommet/hpe-design-system#3739

Screenshots (if appropriate)

Do the grommet docs need to be updated?

yes

Should this PR be mentioned in the release notes?

yes

Is this change backwards compatible or is it a breaking change?

backwards compatible

@britt6612 britt6612 requested a review from taysea March 22, 2024 22:15
@britt6612 britt6612 mentioned this pull request Mar 27, 2024
6 tasks
Brittany Archibeque added 4 commits March 27, 2024 20:10
Signed-off-by: Brittany Archibeque <brittanyarchibeque@Brittanys-MacBook-Pro-2.local>
@britt6612
Copy link
Collaborator Author

britt6612 commented Apr 1, 2024

  • Naming of onChange (technically this is not an onChange event so should we rename it?)
  • Should we have kind on the button to override some of the grommet default behavior. Right now we have
    Kind={theme.toggleGroup} however this opens up more ways to add styles should we just have pad and border in the styledButton UPDATE: decided to go with putting these in styledButton for now to have a more narrow start.
  • Some of the names in theme make sure we all agree and it makes sense. UPDATE: instead of border.color we are going to have divider.color to make more sense to the user in what is changing within the component.

Brittany Archibeque added 4 commits April 2, 2024 13:00
Signed-off-by: Brittany Archibeque <brittanyarchibeque@Brittanys-MacBook-Pro-2.local>
Signed-off-by: Brittany Archibeque <brittanyarchibeque@Brittanys-MacBook-Pro-2.local>
Signed-off-by: Brittany Archibeque <brittanyarchibeque@Brittanys-MacBook-Pro-2.local>
@britt6612 britt6612 requested a review from taysea April 11, 2024 19:24
@britt6612 britt6612 requested a review from taysea April 12, 2024 12:43
@britt6612
Copy link
Collaborator Author

  • Why named “ToggleGroup”

    • When viewing Google search trends it was found that toggleGroup was used more across the wider region.
    • Another downside to using ToggleButtonGroup is how long the word is and in many places in the code base we would need to disable eslint for the imports since the line would be over a amount of characters.
    • Did button actually have any meaning even though this component was made with a Button this did not necessarily need to be made known to the user.
  • We enforce a value once one is selected.

    • 80% of use cases all have one selected option for a toggleGroup.
    • For our specific need for this component a use case was toggling through either a map list or data view where at least one of those needed to be selected.

@taysea taysea requested a review from halocline April 15, 2024 23:27
Copy link
Collaborator

@halocline halocline left a comment

Choose a reason for hiding this comment

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

Functionality is looking good.

I think we need some story cleanup.

@taysea taysea requested a review from halocline April 17, 2024 19:28
iconOnly: {
pad: {
horizontal: 'small',
vertical: 'small',
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm wondering if the height should always be consistent?

Suggested change
vertical: 'small',
vertical: 'xsmall',

Copy link
Collaborator

Choose a reason for hiding this comment

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

Otherwise we'd end up with ragged heights and no alignment with inputs, etc.

Screenshot 2024-04-17 at 2 32 41 PM

Copy link
Collaborator

Choose a reason for hiding this comment

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

Agree they should. I'll update

Copy link
Collaborator

Choose a reason for hiding this comment

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

Refined padding, see "What determined the base.js toggleGroup.button.pad?" in PR description.

@taysea taysea self-requested a review April 18, 2024 14:56
@taysea
Copy link
Collaborator

taysea commented Apr 18, 2024

Dismissing my review, I noticed the button pad + border of container is causing the height to be 2px higher than usual button. Should we be subtracting border width from pad? or should caller be responsible for the calculation?

@taysea
Copy link
Collaborator

taysea commented Apr 18, 2024

Dismissing my review, I noticed the button pad + border of container is causing the height to be 2px higher than usual button. Should we be subtracting border width from pad? or should caller be responsible for the calculation?

After offline discussion, we will align with how Button works --> For "base" theme, the caller is responsible for managing the padding + border calculations. For "kind" themes (like HPE), the button handles the subtraction of border-width from padding. So, for "base" theme, ToggleGroup will base leave responsible for managing.

: themePad;
return edgeStyle('padding', pad, false, undefined, props.theme);
}}
// remove hover style from StyledButton/StyledButtonKind theme
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

👍

@taysea taysea requested a review from halocline April 19, 2024 23:24
@taysea taysea merged commit a8c3b82 into grommet:master Apr 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants