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

Expansion panel behaviour #9427

Open
futbolistua opened this issue Dec 7, 2017 · 28 comments

Comments

@futbolistua
Copy link

commented Dec 7, 2017

At this moment Expansion panel summary handle click to expand/collapse details panel. But I want to put some actions buttons to it. But it's not possible to disable click on the whole summary panel and handle only expandIcon click

@codeskills-nl

This comment has been minimized.

Copy link

commented Dec 9, 2017

For a workaround use event.stopPropagation() at the click event of controls which are on the summary panel.

@futbolistua

This comment has been minimized.

Copy link
Author

commented Dec 12, 2017

Also please remove next style .MuiExpansionPanelSummary-content-35 > :last-child {
/* padding-right: 32px; */
}

@stevewillard

This comment has been minimized.

Copy link
Contributor

commented Jan 4, 2018

@futbolistua were you able to work around this? I'm also trying to prevent the expansion by clicking on the summary (I just want to expand when you click on the icon).

@chathuraa

This comment has been minimized.

Copy link

commented Jan 26, 2018

@futbolistua were you able to find a solution? I'm having the same issue

@stevewillard

This comment has been minimized.

Copy link
Contributor

commented Jan 26, 2018

You can do what @codeskills-nl mentioned

  clickSummary = event => {
    event.stopPropagation();
  };

...

<ExpansionPanel onClick={this.clickSummary}>

@chathuraa

This comment has been minimized.

Copy link

commented Jan 26, 2018

Hi @stevewillard,

Thanks for the suggestion. I have an MUI checkbox in the summary, so when I do stopPropagation, it's messing up check/unchecking the checkbox.

Thanks,
Chat

@Loktor

This comment has been minimized.

Copy link

commented Jan 30, 2018

There is a way to get exactly the behavior you guys want (should also solve your problem @chathuraa):

<ExpansionPanel expanded={this.state.expansionPanelOpen}>
             <ExpansionPanelSummary expandIcon={<ExpandMoreIcon onClick={() => {
               this.setState({
                 expansionPanelOpen: !this.state.expansionPanelOpen
               });
             }}/>}>
               ....

You can manage the ExpansionPanel expanded state yourself, and by using the onClick event of the Icon which is passed to the ExpansionPanelSummary (which is the header line) you can open/close the expansion panel only via the icon)

@TheMoonDawg

This comment has been minimized.

Copy link
Contributor

commented Mar 29, 2018

Amazing workaround Loktor!

@kamranayub

This comment has been minimized.

Copy link
Contributor

commented May 14, 2018

This is pretty common in even regular JS. One way to mitigate this that we're doing is ensuring all input components have a common wrapper. Then you can add the onClick handler to the wrapping element and once the click event reaches it, you can stop propagating.

/* see: https://github.com/mui-org/material-ui/issues/9427 */
const stopPropagation = (e) => e.stopPropagation();
const InputWrapper = ({ children }) =>
  <div onClick={stopPropagation}>
    {children}
  </div>

// usage:

<InputWrapper>
  <MyCoolInput />
</InputWrapper>

This would be a decent way to handle it, if you need to have the summary be clickable. This lets you control propagation selectively if you wish too. Otherwise @Loktor's method works well too, you just might need to override the cursor styles and some attributes because the Summary is still tabbable and has aria roles on it.

@LeoLozes

This comment has been minimized.

Copy link

commented Nov 7, 2018

Thanks @kamranayub your solution is the best here.
It would be nice to have "actions" in the ExpansionPanelSummary (that could be aligned left or right maybe?) where the default behaviour is like this, even if it's easier now with this solution.

@Usama-Tahir

This comment has been minimized.

Copy link

commented Jan 30, 2019

There is a way to get exactly the behavior you guys want (should also solve your problem @chathuraa):

<ExpansionPanel expanded={this.state.expansionPanelOpen}>
             <ExpansionPanelSummary expandIcon={<ExpandMoreIcon onClick={() => {
               this.setState({
                 expansionPanelOpen: !this.state.expansionPanelOpen
               });
             }}/>}>
               ....

You can manage the ExpansionPanel expanded state yourself, and by using the onClick event of the Icon which is passed to the ExpansionPanelSummary (which is the header line) you can open/close the expansion panel only via the icon)

This works fine. But I have an array which I populate using map function. Let's say I have 5 expansion panels and after clicking on the icon button, I change the state as mentioned in your answer. However, the problem with the approach is that it expands all expansion panels but I only want to expand the expansion panel being clicked

@b-ferreira

This comment has been minimized.

Copy link

commented Jan 31, 2019

There is a way to get exactly the behavior you guys want (should also solve your problem @chathuraa):

<ExpansionPanel expanded={this.state.expansionPanelOpen}>
             <ExpansionPanelSummary expandIcon={<ExpandMoreIcon onClick={() => {
               this.setState({
                 expansionPanelOpen: !this.state.expansionPanelOpen
               });
             }}/>}>
               ....

You can manage the ExpansionPanel expanded state yourself, and by using the onClick event of the Icon which is passed to the ExpansionPanelSummary (which is the header line) you can open/close the expansion panel only via the icon)

This works fine. But I have an array which I populate using map function. Let's say I have 5 expansion panels and after clicking on the icon button, I change the state as mentioned in your answer. However, the problem with the approach is that it expands all expansion panels but I only want to expand the expansion panel being clicked

@Usama-Tahir

You can use an arbitrary condition based on a local state to decide what panel will be opened. For example:

state = {
  panel: ''
};

const handleStateChange = panel => () => this.setState({ panel});

// This would be your list of available panels, 
// which I suppose you go through to create your panels.
const panels = {
  panelOne: 'panelOne',
  panelTwo: 'panelTwo',
};

Object.keys(panels).map(panel => (
  <ExpansionPanel expanded={this.state.panel === panel}>
    <ExpansionPanelSummary
      expandIcon={<ExpandMoreIcon onClick={handleStateChange(panel)} />}
    >
      ...
    </ExpansionPanelSummary>
    ...
  </ExpansionPanel>
));
@Usama-Tahir

This comment has been minimized.

Copy link

commented Feb 4, 2019

@b-ferreira thanks

@sibelius

This comment has been minimized.

Copy link

commented Feb 18, 2019

I have a checkbox inside ExpansionPanel

I'm doing this on onChange of Checkbox

event.stopPropagation();
event.preventDefault();

but the ExpansionPanel keep expanding

can I stop this behaviour?

@oliviertassinari

This comment has been minimized.

Copy link
Member

commented Mar 13, 2019

@futbolistua I'm not sure to understand the issue. What's wrong with the controlled behavior? https://next.material-ui.com/demos/expansion-panels/#controlled-accordion. I believe you can control the component, trigger whatever action you like.


Also please remove next style .MuiExpansionPanelSummary-content-35 > :last-child {
/* padding-right: 32px; */
}

Done: #14828.

@Usama-Tahir

This comment has been minimized.

Copy link

commented Mar 19, 2019

let's say I want to open expansion panel by clicking anywhere on the panel (which is the default behavior). But there is a button inside my expansion panel. When I click on that button, I don't want to open the expansion panel. How Can I achieve this? I tried to use z-index but I didn't help.

@b-ferreira

This comment has been minimized.

Copy link

commented Mar 19, 2019

let's say I want to open expansion panel by clicking anywhere on the panel (which is the default behavior). But there is a button inside my expansion panel. When I click on that button, I don't want to open the expansion panel. How Can I achieve this? I tried to use z-index but I didn't help.

@Usama-Tahir it was already covered by this comment from @kamranayub.

@Usama-Tahir

This comment has been minimized.

Copy link

commented Mar 19, 2019

I don't know how that solves my problem.

@oliviertassinari

This comment has been minimized.

Copy link
Member

commented Mar 19, 2019

I would try to do the distinction between event.currentTarget and event.target.

@b-ferreira

This comment has been minimized.

Copy link

commented Mar 19, 2019

@Usama-Tahir if you are talking about having a button into ExpansionPanelSummary which should not open the ExpansionPanel when it's being clicked, then the comment from @kamranayub totally solves your problem.

It's related to the event.stopPropagation() method.

const _handleButtonClick = event => {
  event.stopPropagation();
  ... Do your stuff after here.
}

<ExpansionPanel>
  <ExpansionPanelSummary>
    <Button onClick={_handleButtonClick}>Click me</Button>
  </ExpansionPanelSummary>
  ...
</ExpansionPanel>

This should avoid the ExpansionPanel to being opened when you click onto the Button.

Does that make sense to your issue?

@Usama-Tahir

This comment has been minimized.

Copy link

commented Mar 21, 2019

yes, That solved my problem partially. Actually, I render expansion panels from an array. Initially, I was using expanded prop inside <ExpansionPanel /> to open it. So when I clicked on another expansion panel, it would close any other opened expansion panel as well. I can't figure out how to achieve this with the current solution you provided?

@oliviertassinari

This comment has been minimized.

Copy link
Member

commented Mar 21, 2019

What do you guys think of this approach?

--- a/docs/src/pages/demos/expansion-panels/ControlledExpansionPanels.js
+++ b/docs/src/pages/demos/expansion-panels/ControlledExpansionPanels.js
@@ -1,8 +1,8 @@
 import React from 'react';
-import { makeStyles } from '@material-ui/core/styles';
+import { makeStyles, withStyles } from '@material-ui/core/styles';
 import ExpansionPanel from '@material-ui/core/ExpansionPanel';
 import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
-import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
+import MuiExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
 import Typography from '@material-ui/core/Typography';
 import ExpandMoreIcon from '@material-ui/icons/ExpandMore';

@@ -21,18 +21,28 @@ const useStyles = makeStyles(theme => ({
   },
 }));

+const ExpansionPanelSummary = withStyles({
+  root: {
+    cursor: 'default',
+  },
+})(MuiExpansionPanelSummary);
+
 function ControlledExpansionPanels() {
   const classes = useStyles();
   const [expanded, setExpanded] = React.useState(null);

-  const handleChange = panel => (event, isExpanded) => {
-    setExpanded(isExpanded ? panel : false);
+  const handleChange = panel => () => {
+    const isExpanded = expanded === panel;
+    setExpanded(isExpanded ? false : panel);
   };

   return (
     <div className={classes.root}>
-      <ExpansionPanel expanded={expanded === 'panel1'} onChange={handleChange('panel1')}>
+      <ExpansionPanel expanded={expanded === 'panel1'}>
         <ExpansionPanelSummary
+          IconButtonProps={{
+            onClick: handleChange('panel1'),
+          }}
           expandIcon={<ExpandMoreIcon />}
           aria-controls="panel1bh-content"
           id="panel1bh-header"
@@ -47,8 +57,11 @@ function ControlledExpansionPanels() {
           </Typography>
         </ExpansionPanelDetails>
       </ExpansionPanel>
-      <ExpansionPanel expanded={expanded === 'panel2'} onChange={handleChange('panel2')}>
+      <ExpansionPanel expanded={expanded === 'panel2'}>
         <ExpansionPanelSummary
+          IconButtonProps={{
+            onClick: handleChange('panel2'),
+          }}
           expandIcon={<ExpandMoreIcon />}
           aria-controls="panel2bh-content"
           id="panel2bh-header"
@@ -65,8 +78,11 @@ function ControlledExpansionPanels() {
           </Typography>
         </ExpansionPanelDetails>
       </ExpansionPanel>
-      <ExpansionPanel expanded={expanded === 'panel3'} onChange={handleChange('panel3')}>
+      <ExpansionPanel expanded={expanded === 'panel3'}>
         <ExpansionPanelSummary
+          IconButtonProps={{
+            onClick: handleChange('panel3'),
+          }}
           expandIcon={<ExpandMoreIcon />}
           aria-controls="panel3bh-content"
           id="panel3bh-header"
@@ -83,8 +99,11 @@ function ControlledExpansionPanels() {
           </Typography>
         </ExpansionPanelDetails>
       </ExpansionPanel>
-      <ExpansionPanel expanded={expanded === 'panel4'} onChange={handleChange('panel4')}>
+      <ExpansionPanel expanded={expanded === 'panel4'}>
         <ExpansionPanelSummary
+          IconButtonProps={{
+            onClick: handleChange('panel4'),
+          }}
           expandIcon={<ExpandMoreIcon />}
           aria-controls="panel4bh-content"
           id="panel4bh-header"
diff --git a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js
index 388667124..4ea5a86f1 100644
--- a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js
+++ b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js
@@ -19,9 +19,7 @@ export const styles = theme => {
       minHeight: 8 * 6,
       transition: theme.transitions.create(['min-height', 'background-color'], transition),
       padding: '0 24px 0 24px',
-      '&:hover:not($disabled)': {
-        cursor: 'pointer',
-      },
+      cursor: 'pointer',
       '&$expanded': {
         minHeight: 64,
       },
@@ -30,6 +28,7 @@ export const styles = theme => {
       },
       '&$disabled': {
         opacity: 0.38,
+        cursor: 'default',
       },
     },
     /* Styles applied to the root element, children wrapper element and `IconButton` component if `expanded={true}`. */

https://codesandbox.io/s/jjy809l6ny

I think that it would make a great demo. Does anyone want to handle it?

@oliviertassinari

This comment has been minimized.

Copy link
Member

commented Mar 21, 2019

So when I clicked on another expansion panel, it would close any other opened expansion panel as well.

Oh, in this case, maybe we should add another property to handle the style change and click behavior. It would make it even simpler. I don't know. At least we can simplify the point style override:

--- a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js
+++ b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js
@@ -19,9 +19,7 @@ export const styles = theme => {
       minHeight: 8 * 6,
       transition: theme.transitions.create(['min-height', 'background-color'], transition),
       padding: '0 24px 0 24px',
-      '&:hover:not($disabled)': {
-        cursor: 'pointer',
-      },
+      cursor: 'pointer',
       '&$expanded': {
         minHeight: 64,
       },
@@ -30,6 +28,7 @@ export const styles = theme => {
       },
       '&$disabled': {
         opacity: 0.38,
+        cursor: 'default',
       },
     },
     /* Styles applied to the root element, children wrapper element and `IconButton` component if `expanded={true}`. */
@IssueHuntBot

This comment has been minimized.

Copy link

commented Apr 8, 2019

@0maxxam0 has funded $2.00 to this issue.


@danielo515

This comment has been minimized.

Copy link

commented May 29, 2019

This issue is still a problem.
Expansion panel summary it's a button, hence it is intercepting all kind of click events. This is even worse on mobile platforms.
I think that the container should have a way to provide controls to the expansion panel, I think is something common.

@oliviertassinari

This comment has been minimized.

Copy link
Member

commented May 29, 2019

@danielo515 What do you think of my proposed solution?

@danielo515

This comment has been minimized.

Copy link

commented May 31, 2019

@oliviertassinari your proposals seems to be focused on solving a different problem.
My problem is that the ExpanionPanelSummary is a giant button. This makes impossible that any of it's children detect click events properly because his parent is "absorbing" them.

@oliviertassinari

This comment has been minimized.

Copy link
Member

commented May 31, 2019

@danielo515 I believe we are talking about the same problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.